Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/20596-service-datasource-provenance-anchors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@objectstack/service-datasource': patch
---

Provenance comments in `service-datasource` were re-anchored

Comment and docblock lines under `src/` that cited tracker numbers which no
longer resolve on GitHub now cite the commit in this repository's history that
decided the matter, and say in their own words what was decided. Comments
only: no type, schema, export, log or refusal text, or runtime behaviour changes.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
* turns exactly that into `AuthzStoreUnavailableError`: declared `status:
* 503`, declared `code: SERVICE_UNAVAILABLE`.
* 3. `requireDatasourceAdmin`'s own `catch` re-raises it rather than
* laundering an outage into a denial — the #13279 ruling, which this card
* laundering an outage into a denial — commit 6a180e42d's ruling, which this card
* leaves completely untouched. Only the RENDERING moves.
* 4. The throw escapes the handler. Before this card the adapter answered
* `500 { code: 'INTERNAL_ERROR', message: 'No response from handler' }` —
Expand All @@ -50,7 +50,7 @@
* ⭐ The controls answering `401` rather than `200` is the SHARPER reading, and
* it is the discriminator this card is actually about: a 401 is a VERDICT the
* door reached, while the outage arm reaches no verdict at all — which is
* exactly why #13279 refuses to answer it as a denial. Two different 4xx/5xx
* exactly why commit 6a180e42d refuses to answer it as a denial. Two different 4xx/5xx
* answers separated by whether a decision was ever taken.
*/

Expand Down Expand Up @@ -159,7 +159,7 @@ describe('GET /api/v1/datasources — an authz-store outage reaches the caller a
const { listDatasources } = await list({ kind: 'throws' });

// The card moves the RENDERING only. If the outage started resolving to a
// verdict, this would be non-zero and #13279 would have been reversed as a
// verdict, this would be non-zero and commit 6a180e42d would have been reversed as a
// rider.
expect(listDatasources).not.toHaveBeenCalled();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,7 @@ describe('[#15350] §5b — a `tenancy` service that was REGISTERED and FAILED i
// handler"), not the `503 SERVICE_UNAVAILABLE` the brand carries. That is a
// PRE-EXISTING relay gap, not one this card opened — `requireDatasourceAdmin`
// has re-raised `AuthzStoreUnavailableError` for the identical `ql`
// permission-store outage since #13279, out of route handlers that have no
// permission-store outage since commit 6a180e42d, out of route handlers that have no
// `catch`, and the Hono adapter renders any escaped throw as a bare 500. It
// is filed separately. Asserting the class rather than the digits keeps this
// pin measuring the SECURITY property — the outage is never answered as an
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

/**
* #8696 — a bound `external.credentialsRef` reaches the client on the mysql
* Commit 72050cc47 — a bound `external.credentialsRef` reaches the client on the mysql
* arm's DSN branch, not only on its discrete-fields branch.
*
* ## The defect
Expand Down Expand Up @@ -69,7 +69,7 @@
* The first failure is the whole defect in one line: the arm answered with the
* DSN *string*, which has no key for a credential to live in.
*
* ## The mongodb half, added second (#8696's remaining arm)
* ## The mongodb half, added second (commit 90a12fb18, the remaining arm)
*
* `buildMongoUrl`'s `if (explicit) return explicit;` dropped the bound secret
* the same way, and is closed by `buildMongoAuth` — `options.auth` beside an
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

/**
* [#12010] The seam pin for `ConnectionEngineLike`.
* [commit 77b91bdb4] The seam pin for `ConnectionEngineLike`.
*
* `ConnectionEngineLike` is the exported view `DatasourceConnectionService`
* drives the ObjectQL `'data'` engine through. It used to re-declare seven
Expand Down Expand Up @@ -55,7 +55,7 @@ describe('ConnectionEngineLike is the contract, not a fork of it (#12010)', () =
it('refuses a value that is not a driver, at the call site', () => {
const engine = {} as ConnectionEngineLike;
// @ts-expect-error - a bare `{ name }` is not an `IDataDriver`. This call
// compiled before #12010, which is precisely the hole: the seam promised
// compiled before commit 77b91bdb4, which is precisely the hole: the seam promised
// the engine accepts any value as a driver, and it does not.
engine.registerDriver?.({ name: 'com.example.not-a-driver' });
expect(engine).toBeTruthy();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ describe('migrateCredential (#8155)', () => {

/**
* The contract half the #8153 block was about: the row this migration WRITES
* must be spec-valid. Before PR #8588 a managed row carrying
* must be spec-valid. Before commit 3dede582b a managed row carrying
* `external.credentialsRef` failed re-parse, so the migration would have moved
* rows from "invalid because it holds cleartext" to "invalid because it holds a
* credentialsRef" while reporting success.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ describe('GREEN ON MAIN — #8078 is not weakened by anything above', () => {
});

describe('#9040 — the passthrough spelling, both halves at the service door', () => {
/** A legacy mongo row written before #9040: the password rides the MongoClient passthrough. */
/** A legacy mongo row written before commit 24206416a: the password rides the MongoClient passthrough. */
const LEGACY_MONGO: StoredDatasource = {
name: 'legacy_mongo',
driver: 'mongodb',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function fakeEngine() {
registerDatasourceDef: (def) => {
defs.push(def);
},
// [#12010] The double deliberately stores MINIMAL stand-ins (a bare
// [commit 77b91bdb4] The double deliberately stores MINIMAL stand-ins (a bare
// `{ name }` is how these tests simulate an `onEnable`-registered
// driver), while the derived seam member answers the contract's
// `IDataDriver | undefined`. Narrowing on the way out keeps the double
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ describe('#5714 — which driver arms read a declared `pool`', () => {
// literal as it stood on `origin/main` before this change, because "we only
// added an arm" is a claim about bytes.
// Byte-for-byte as #5714 wrote it, with ONE word changed: the closing clause
// names the pooled drivers, and #6345 renamed the canonical mongo id to
// names the pooled drivers, and commit e2798fab7 renamed the canonical mongo id to
// `mongodb`. Naming the retired canon in an instruction the author is meant to
// act on would send them to a spelling the catalog no longer publishes.
it('leaves the sqlite arms\' message byte-for-byte as #5714 wrote it', () => {
Expand Down Expand Up @@ -305,7 +305,7 @@ function fakeEngine() {
drivers,
registerDriver: (driver: any) => { drivers.set(driver.name, driver); },
registerDatasourceDef: () => {},
// [#12010] The double deliberately stores MINIMAL stand-ins (a bare
// [commit 77b91bdb4] The double deliberately stores MINIMAL stand-ins (a bare
// `{ name }` is how these tests simulate an `onEnable`-registered
// driver), while the derived seam member answers the contract's
// `IDataDriver | undefined`. Narrowing on the way out keeps the double
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ describe('createDefaultDatasourceDriverFactory — legacy config spellings are n
// #7314 — the OPTIONAL libSQL driver is missing, and until now this arm said so
// and stopped: `turso driver requested but @objectstack/driver-turso is not
// installed (…)`. The HOST loader (`@objectstack/runtime`'s
// `loadTursoDriverFactory`, single owner since #6268) has answered the same
// `loadTursoDriverFactory`, single owner since commit 68f5eccb1) has answered the same
// missing package with the install command, the consequence and the reason for
// refusing since #5602 — so the SAME fault got two qualities of answer depending
// on whether the datasource happened to be the host's `default` (told how to fix
Expand Down Expand Up @@ -437,7 +437,7 @@ describe('createDefaultDatasourceDriverFactory — the missing libSQL package is
});

it('is what the turso arm actually raises when the optional package is absent', async () => {
// ⭐ STAGED absence since #12943 — this case used to reach the arm with NO
// ⭐ STAGED absence since commit 090f2302e — this case used to reach the arm with NO
// stub, and that is no longer possible. `@objectstack/driver-turso` is now
// an OPTIONAL PEER of `@objectstack/service-datasource`
// (`peerDependencies` + `peerDependenciesMeta.optional`): the honest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('DRIVER_CATALOG', () => {
expect(entry.description, entry.id).toBeTruthy();
expect(entry.icon, entry.id).toBeTruthy();
}
// `mongodb`, not `mongo`, since #6345 renamed the canonical driver id. This
// `mongodb`, not `mongo`, since commit e2798fab7 renamed the canonical driver id. This
// list is the PUBLISHED contract Studio writes into `datasource.driver`, so
// the assertion is the one that has to move with the rename — stored rows
// carrying `mongo` are converged by the ADR-0087 conversion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ describe('validateAll', () => {
});

/**
* [#11166] The card's measured defect, reproduced: an introspector throwing
* [commit 735f5c709] Its card's measured defect, reproduced: an introspector throwing
* `connect ECONNREFUSED 10.0.0.5:5432` used to come back as
* `kind: 'missing_table'` — indistinguishable from a genuinely dropped
* table, and an abort under the boot gate's default `onMismatch: 'fail'`.
Expand Down Expand Up @@ -490,7 +490,7 @@ describe('validateAll', () => {
});

/**
* [#11166] The other half of the distinction: a table genuinely absent from
* [commit 735f5c709] The other half of the distinction: a table genuinely absent from
* a schema that WAS read stays `missing_table` — the classification change
* must not blur the measured fact into the indeterminate kind.
*/
Expand All @@ -514,7 +514,7 @@ describe('validateAll', () => {
});

/**
* [#10537] `validateDatasource` — the same sweep, scoped to one datasource.
* [commit e634ecf6a] `validateDatasource` — the same sweep, scoped to one datasource.
*
* The card it closes is a COST/SHAPE defect, not a wrong answer:
* `POST /datasources/:name/external/validate` used to run `validateAll()` and
Expand Down Expand Up @@ -585,7 +585,7 @@ describe('validateDatasource', () => {
return { svc, introspected };
}

/** What the pre-#10537 route computed: the whole sweep, filtered afterwards. */
/** What the route computed before commit e634ecf6a: the whole sweep, filtered afterwards. */
async function sweptThenFiltered(datasource: string, opts?: { unreachable?: readonly string[] }) {
const { svc, introspected } = makeMulti(opts);
const report = await svc.validateAll();
Expand Down Expand Up @@ -649,7 +649,7 @@ describe('validateDatasource', () => {

// The scoped path still turns a per-object throw into a row rather than
// rejecting the whole report — the sweep's `catch`, not a second one.
// [#11166] The row's kind is `unreachable` (the fixture's introspector
// [commit 735f5c709] The row's kind is `unreachable` (the fixture's introspector
// threw a connection error), no longer the invented `missing_table`.
expect(introspected).toEqual(['wh_b']);
expect(report.ok).toBe(false);
Expand All @@ -673,7 +673,7 @@ describe('validateDatasource', () => {
});

/**
* [#10962] Per-sweep introspection memo — the CALL COUNT is the deliverable.
* [commit 29d067646] Per-sweep introspection memo — the CALL COUNT is the deliverable.
*
* `validateObject` reads the live remote schema on every call, so a sweep over
* M federated objects on one datasource used to perform M concurrent
Expand Down Expand Up @@ -810,7 +810,7 @@ describe('per-sweep introspection memo [#10962]', () => {
datasource: M_DATASOURCE,
diffs: [
expect.objectContaining({
// [#11166] A throw out of introspect is `unreachable`, never an
// [commit 735f5c709] A throw out of introspect is `unreachable`, never an
// invented `missing_table` — this pin is about the COUNT (one
// shared connection attempt), and rides the kind ruling as-is.
kind: 'unreachable',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

/**
* #8874 — a declared `ssl` reaches the mysql CLIENT on both branches of the
* Commit d70428ae7 — a declared `ssl` reaches the mysql CLIENT on both branches of the
* mysql arm, in the spelling mysql2 can actually read.
*
* ## The defect, in two halves
Expand Down Expand Up @@ -37,7 +37,7 @@
* {uri:'mysql://app@db.internal:3306/app', ssl:{}} -> ssl {rejectUnauthorized:true}
* ```
*
* The branch #8874 describes as "honouring" the declaration was therefore
* The branch commit d70428ae7's card describes as "honouring" the declaration was therefore
* throwing on every connection acquisition for the commonest way of declaring
* it. Emitting `ssl: true` onto the DSN branch to close the first half would
* have shipped that throw to a second branch, so `mysqlSslOption` translates
Expand All @@ -50,7 +50,7 @@
* the mysql2 module knex itself resolved (`knex.client.driver`), fed the exact
* `connectionSettings` knex will hand it. Nothing asserts on the `connection`
* object this factory emitted, and that is deliberate: it is the constraint
* inherited from #8873, where the postgres arm passed the equivalent
* inherited from commit 096106522, where the postgres arm passed the equivalent
* config-layer assertion throughout the defect's entire life while the client
* threw the value away one layer below. Here it is the sharper of the two
* lessons, because the boolean half of this card is invisible at the config
Expand Down Expand Up @@ -186,7 +186,7 @@ describe('#8874 — mysql: a declared `ssl` reaches the client on the DSN branch
});

it('carries TLS and a bound secret together on the DSN branch', async () => {
// The other DSN sub-case. #8696 made this branch return `{ uri, password }`
// The other DSN sub-case. Commit 72050cc47 made this branch return `{ uri, password }`
// and deliberately left `ssl` out of it, because carrying TLS only for
// datasources that happen to bind a credential would have been a second,
// stranger asymmetry. Both channels now ride together.
Expand Down Expand Up @@ -260,7 +260,7 @@ describe('#8874 — mysql: a declared `ssl` reaches the client on the DSN branch
it('leaves a DSN with only a secret bound as the #8696 shape (control)', async () => {
// Green before this change and after it. The `ssl` key must not appear on a
// connection that declared none — otherwise this card would have widened
// #8696's blast radius rather than added to it.
// commit 72050cc47's blast radius rather than added to it.
const conn = await emittedConnection({
name: 'secret-only',
config: { url: BARE_USERNAME_DSN },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

/**
* #8873 — a bound `external.credentialsRef` reaches the SERVER on the postgres
* Commit 096106522 — a bound `external.credentialsRef` reaches the SERVER on the postgres
* arm's DSN branch, not merely the knex config.
*
* ## The defect, and why it survived a passing sibling pin
Expand Down Expand Up @@ -220,7 +220,7 @@ describe('#8873 — postgres: a bound secret reaches the CLIENT on the DSN branc
// password only when the server asks for one — so injecting cannot break a
// datasource that connects today, and refusing would silently drop a
// credential the operator bound. Making the contradictory pair loud belongs
// at the authoring door (#9041), which this card lands before.
// at the authoring door (commit d491625c1), which landed after this pin.
const resolved = await pgResolved({
name: 'anonymous-url',
config: { url: 'postgresql://db.internal:5432/app' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ describe('createPrebuiltDriverFactory — through DatasourceConnectionService (t
drivers.set(d.name, d);
if (isDefault) defaultName = d.name;
},
// [#12010] The double deliberately stores MINIMAL stand-ins (a bare
// [commit 77b91bdb4] The double deliberately stores MINIMAL stand-ins (a bare
// `{ name }` is how these tests simulate an `onEnable`-registered
// driver), while the derived seam member answers the contract's
// `IDataDriver | undefined`. Narrowing on the way out keeps the double
Expand Down
4 changes: 2 additions & 2 deletions packages/services/service-datasource/src/admin-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ export function registerDatasourceAdminRoutes(
* The throw is raised inside `requireDatasourceAdmin`'s own `try`, so it
* takes the relay that block already runs for the identical fault one seam
* over: `isAuthzStoreUnavailableError(err)` re-raises it rather than
* laundering an outage into a denial (#13279). Deliberately NOT a new relay.
* laundering an outage into a denial (commit 6a180e42d). Deliberately NOT a new relay.
*
* ## Why `getServiceAsync`, and why its ABSENCE is quiet
*
Expand Down Expand Up @@ -487,7 +487,7 @@ export function registerDatasourceAdminRoutes(
systemPermissions = Array.isArray(authz.systemPermissions) ? authz.systemPermissions : [];
}
} catch (err) {
// [#13279] "grants that could not be READ are not grants" was the exact
// [commit 6a180e42d] "grants that could not be READ are not grants" was the exact
// reasoning the 2026-08-30 ruling reverses: an unreadable store licenses
// no verdict at all, so the outage is re-raised instead of being answered
// as a denial. Every other fault still fails closed, unchanged.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,13 @@ export interface DatasourceBoundObject {
* #4251 B3 sweep pattern, applied here exactly as `datasource-admin-plugin.ts`
* applied it to its own `DataEngineLike` one file over, under #11493's ruling.
*
* [#12010] Every member was hand-written here until this change, and the
* inventory that filed that card measured what it cost. Three of them —
* [commit 77b91bdb4] Every member was hand-written here until this change, and the
* inventory that filed its card measured what it cost. Three of them —
* `registerDatasourceDef`, `markDatasourceUnavailable`,
* `clearDatasourceUnavailable` — were declared by NO contract at all: real
* `ObjectQL` methods, called across a package boundary, meeting no compiler on
* the producer side, so drift landed silently in this consumer. #12248
* adjudicated all three onto {@link IDataEngine} and #12482 followed with
* the producer side, so drift landed silently in this consumer. Commit 8425c17cc
* adopted all three onto {@link IDataEngine} per the ruling, and #12482 followed with
* `syncObjectSchema`; deriving is what makes the next drift a build error here
* instead of a re-declaration that quietly disagrees.
*
Expand Down Expand Up @@ -606,7 +606,7 @@ export class DatasourceConnectionService {
// `default` goes through the engine's default-driver fallback, never
// `drivers.get('default')`, and the natural name keeps logs/lookups
// byte-for-byte with the pre-#3826 boot.
// [#12010] `DatasourceDriverHandle.driver` is declared `unknown` — the
// [commit 77b91bdb4] `DatasourceDriverHandle.driver` is declared `unknown` — the
// factory escape hatch is open to any host-built driver — so a cast is
// unavoidable somewhere on this path. It belongs HERE, at the one call
// site that constructs the value, not widened into the exported seam
Expand Down Expand Up @@ -697,7 +697,7 @@ export class DatasourceConnectionService {
async disconnect(name: string, opts: { asDefault?: boolean } = {}): Promise<void> {
const engine = this.cfg.engine();
const driverName = opts.asDefault ? engine?.getDefaultDriverName?.() : name;
// [#12010] Cast-free: `getDriverByName` now answers the contract's
// [commit 77b91bdb4] Cast-free: `getDriverByName` now answers the contract's
// `IDataDriver | undefined` instead of a locally re-declared `unknown`.
// The `typeof … === 'function'` guard below stays — a host-built driver in
// the registry satisfies the contract only structurally.
Expand Down
Loading
Loading