@githolon/dsl 0.95.2 → 0.96.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@githolon/dsl",
3
- "version": "0.95.2",
3
+ "version": "0.96.0",
4
4
  "type": "module",
5
5
  "description": "Nomos 2 domain-authoring DSL: aggregates + directives in TS, executed and encoded to the Rust kernel's wire shapes.",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -75,6 +75,7 @@ import { collectValueObjectDescriptors } from "./value_object.js";
75
75
  import { emitUsd } from "./usd.js";
76
76
  import { expandCapabilityExports } from "./capability_exports.js";
77
77
  import { RelationTuple, relationTuplesByObject } from "./framework/relations.js";
78
+ import { AuthProvider, setAuthProvider, authProvider } from "./framework/workspaces.js";
78
79
  import { parentAttestedBirthMarker } from "./births.js";
79
80
  import { discoverUnsanctionedEnsures, sanctioningEnsureDirective } from "./secondary_ensure.js";
80
81
  import { assertIdOfLint } from "./idof_lint.js";
@@ -209,7 +210,24 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
209
210
  // insertion order does not perturb the hash. Era-safe: only a relation-using domain gains the aggregate,
210
211
  // so no existing (relation-free) manifest hash drifts, and a tenant that already had the explicit export
211
212
  // sees NO further move (identical bytes).
213
+ // AUTH-PROVIDER AUTO-COMPOSE — the "a private workspace can always declare a verifier" DX fix (kills the
214
+ // `no-authprovider-verifier` dead-workspace trap). A package declaring ANY private read (an aggregate
215
+ // `visibility: requires(cap)` — also the ONLY host for `.encrypted()` fields) MUST be able to author its
216
+ // identity verifier, else it is born PERMANENTLY unable to verify a reader's token. Auto-carry the framework
217
+ // `AuthProvider` aggregate + the admin-gated `setAuthProvider` directive (its `.requires("admin")` gate
218
+ // travels with it, so a SHARED private workspace can't let a non-admin swap the verifier) + the
219
+ // `authProvider` query — exactly as RelationTuple is carried. Placed BEFORE the usesRelations check so
220
+ // setAuthProvider's admin gate pulls RelationTuple in too. ERA-SAFE: a PUBLIC package declares no private
221
+ // aggregate, gains nothing, stays byte-identical; a private package recompiled under this DSL moves hash anyway.
222
+ const declaresPrivateRead = [...byId.values()].some(
223
+ (a) => (a as { visibility?: unknown }).visibility !== undefined,
224
+ );
225
+ if (declaresPrivateRead) {
226
+ if (!byId.has(AuthProvider.id)) byId.set(AuthProvider.id, AuthProvider);
227
+ if (!directives.some((d) => d.id === setAuthProvider.id)) directives.push(setAuthProvider);
228
+ }
212
229
  const usesRelations =
230
+ declaresPrivateRead ||
213
231
  (spec.relationTypes ?? []).length > 0 ||
214
232
  directives.some((d) => (d as { requiresRelationGate?: unknown }).requiresRelationGate !== undefined) ||
215
233
  directives.some((d) => d.aggregateId === RelationTuple.id);
@@ -311,6 +329,11 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
311
329
  // `usesRelations` was computed above the aggregate snapshot (so RelationTuple could be injected into the
312
330
  // schema); reuse it here to append the audit query. The two MUST agree — the aggregate and the query it
313
331
  // returns have to enter the manifest together, or the read projection can't resolve the return schema.
332
+ // Auth-provider auto-compose (sibling of the RelationTuple query carry): a private package gets the
333
+ // `authProvider` read so the workspace can surface its declared verifier.
334
+ if (declaresPrivateRead && !queries.some((q) => q.id === authProvider.id)) {
335
+ queries.push(authProvider);
336
+ }
314
337
  if (usesRelations && !queries.some((q) => q.id === relationTuplesByObject.id)) {
315
338
  queries.push(relationTuplesByObject);
316
339
  }