Osy#betaRun it on Osyrin: osyrin.com

Reference / Project

Bridges — a package that arrives when an app uses the two it completes

package Owner.A.B { completes Owner.A@0, Owner.B@0; use Owner.A@0; use Owner.B@0; } · app X { without Owner.A.B; }

A bridge is a package whose manifest says `completes A@0, B@0;`. An app that uses every package it completes gets it without writing a `use` line: `osy lock` finds it, pins it, and says so. `without` in the app manifest keeps one out.

stable1 example compiled by CIprojectpackageskitsbridges

Summary#

Two kits that know nothing of each other often have something to say together: privacy requests that reach a desk's conversations, an inbox whose people are told in the bell, a shop that checks age with BankID. That code lives in a bridge — a package that uses both and declares it completes them. An app that uses both kits gets the bridge by itself. Nobody has to know the bridge exists, and nothing breaks for an app that uses only one of the two.

app Sales {
  model "model/**/*.osy";
  // A bridge that would arrive with this app's packages, kept out — `osy lock` says which bridges arrive.
  without Acme.Crm.Cpq;
}

entity Lead {
  [Required, MaxLength(120)] string Name;
}

Signature#

// package.osy of a bridge
package Owner.A.B {
  version   "0.1.0";
  completes Owner.A@0, Owner.B@0;   // each one a package this bridge uses, at that major
  use Owner.A@0;
  use Owner.B@0;
  model "model/*.osy";
}

// app.osy of an app that keeps it out
app X { without Owner.A.B; }

Description#

What makes a package a bridge?#

One line in its package.osy: completes, then the packages it completes, each with the major it was written for. Each one must be a package the bridge itself uses, at that same major — a bridge composes over what it names, so it has to depend on it. The compiler refuses anything else, naming the line to write:

refusalwhen
PACKAGE_COMPLETES_UNUSEDa package the bridge completes is not one of its use lines
PACKAGE_COMPLETES_MAJORthe major differs from the one the bridge uses
PACKAGE_COMPLETES_NO_MAJORa package is named without its @major
PACKAGE_COMPLETES_ONEonly one package is named — the bridge would arrive in every app that uses it; an add-on an app chooses is an ordinary package with a use line
PACKAGE_COMPLETES_REPEATED · PACKAGE_COMPLETES_ITSELFa package named twice, or the bridge naming itself

A bridge may complete three packages; it arrives when an app uses all three.

When does a bridge arrive?#

When an app's own use lines include every package the bridge completes, at the majors it names. A tests use line does not count — the app you deploy is the same with or without its tests. osy lock asks every catalog the project resolves packages from which packages complete the ones the app uses, resolves each one it is offered, reads the bridge's own manifest, and includes the bridge when its whole completes set is there:

$ osy lock
✓ Wrote osyrin.lock (54 pins)
  …
+ Osysharp.WorkQueues.Notifications 0.2.0 included: completes Osysharp.WorkQueues + Osysharp.Notifications (keep it out with `without Osysharp.WorkQueues.Notifications;` in app.osy)
+ Osysharp.Privacy.UserAccounts 0.2.0 included: completes Osysharp.Privacy + Osysharp.UserAccounts (keep it out with `without Osysharp.Privacy.UserAccounts;` in app.osy)
  · Osysharp.Privacy.Sms not included — completes Osysharp.Privacy + Osysharp.Sms, and this app does not use `Osysharp.Sms`.
  bridges searched in workspace, platform, github

An included bridge is a use in every respect but its spelling: it is composed, the app may import its names with using, and it is installed and granted like any other. An app that sets a bridge's options names its setting class as usual — app.CrmCpq = new CrmCpqSetup { … }; — and writes nothing else. A bridge that needs a host or a secret is granted on a use line of its own: write use Owner.A.B@0 { egress "…"; }, and it is then the app's choice like any package it writes.

What does the lock record?#

The included bridge's entry says why it is there, and a bridges record says what the search saw — which catalogs answered, which were skipped, what was offered and not included, and what the app left out:

{
  "name": "Osysharp.WorkQueues.Notifications",
  "kind": "package",
  "version": "0.2.0",
  "hash": "sha256:…",
  "source": "workspace:kits/work-queues-notifications",
  "included": "completes Osysharp.WorkQueues + Osysharp.Notifications"
},
…
"bridges": {
  "searched": ["workspace", "platform", "github"],
  "notIncluded": [
    { "name": "Osysharp.Privacy.Sms",
      "reason": "completes Osysharp.Privacy + Osysharp.Sms, and this app does not use `Osysharp.Sms`." }
  ],
  "leftOut": ["Acme.Crm.Cpq"]
}

osy lock is the only verb that searches. Every compile after it — osy validate, osy test, osy compile, a deploy, the server — reads the bridges from the pinned packages themselves, and never reaches a network for them. A compile that re-pins a package changed in your own repository keeps the lock's bridges as they are, re-checking each against the app's use and without lines. To look again — a bridge was published since — run osy lock.

Where are bridges found?#

In every place the project resolves packages from, in the same order a use resolves:

catalogwhat it holds
workspacethe packages in your own repository — its kits/*, and whatever an osy.workspace lists
platformthe packages the platform ships with the CLI
githubevery published package: a repository carrying the osy-package topic. osy publish gives a bridge one more topic per package it completes — osy-completes-osysharp-inbox — and the lock reads them all in one search, then asks only a matching repository for its releases. When GitHub's search does not answer (rate-limited, down), the published package index is read instead, and the lock records that it was

An organisation's own catalog answers the same question when it is configured. A catalog that could not be asked is recorded under skipped, so a reader of the lock knows a bridge published there may be missing from it.

Locking without a network#

osy lock --offline searches only the workspace and the platform's packages for bridges, and records that the public search was skipped:

"bridges": {
  "searched": ["workspace", "platform"],
  "skipped": ["github: offline — the public catalog was not searched for bridges. Run `osy lock` with a network to look."]
}

How do I keep a bridge out?#

without <Package>; in the app { } manifest — the example above. The bridge is not pinned, not composed, and its names cannot be imported; the lock lists it under leftOut. A without that names a package the manifest also uses is refused (WITHOUT_AND_USE): one line has to go. A without that names no bridge any catalog offered leaves nothing out, and the lock says so under notIncluded, so a typo is not silent.

⚠ This is the top-level without, which keeps a whole package out. Inside a use body, without leaves out one of that package's contributions to a page ([Slot] and contributions — a place on a page that other kits, and the app, add to).

Why did a bridge not arrive?#

Read the lock's notIncluded list: each bridge a catalog offered carries the reason — a package it completes that this app does not use, or one it completes at another major (it completes Acme.Crm@1, and this app has Acme.Crm 0.3.0 — another major, which this 0.1.0 was not written for). If it is not listed at all, no catalog offered it: run osy lock with a network, and check the bridge is published with its osy-completes-… topics.

Publishing a bridge#

osy publish sets osy-package and one osy-completes-<owner>-<name> topic per package the bridge completes, beside any topics the repository already has. A topic is at most 50 characters; a name too long for that ends in a short hash of the full name, computed the same way by the publisher and the lock. Check a bridge composes with Checking a kit composes — osy kits check before publishing it.

See also#

Related

package.osy

The manifest that makes a git repository a publishable Osy# package. It names the package, says which of the…

app.osy

The manifest at the root of every project. It names the app, says which files are model, seed, migrations and tests…

Contracts between kits — an interface in a package, and its version

A kit that needs something done declares an ordinary public interface; another kit implements it; the app picks one in…

Checking a kit composes — osy kits check

Checks that a kit composes with the kits around it: it uses no other product kit it does not complete…

Extension members

A `partial entity X { … }` in a bridge package or in the app adds OPTIONAL members to an entity another package…

Finding a kit, and learning one from its tests

Lists the kits (packages) this project can build on — each one's contracts, the kits that implement them, and what this…