@githolon/dsl 0.103.12 → 0.103.13

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.
Files changed (43) hide show
  1. package/dart/CHANGELOG.md +5 -0
  2. package/dart/lib/src/runner/node-entry.mjs +234 -170
  3. package/dart/lib/src/runner/offline-install.mjs +87 -112
  4. package/dart/lib/src/webview_bridge.dart +11 -11
  5. package/dart/pubspec.yaml +1 -1
  6. package/dart/test/webview_bridge_test.dart +10 -7
  7. package/dart_types/CHANGELOG.md +1 -1
  8. package/dart_types/lib/nomos_types.dart +4 -4
  9. package/dart_types/lib/src/versioned_nomos_ref.dart +13 -11
  10. package/package.json +1 -1
  11. package/runtime/generated-application-host.js +424 -237
  12. package/src/aggregate.ts +3 -1
  13. package/src/build_package.ts +77 -6
  14. package/src/check_package_main.ts +39 -16
  15. package/src/codegen_dart.ts +185 -48
  16. package/src/codegen_dart_application.ts +82 -16
  17. package/src/codegen_dart_client.ts +38 -10
  18. package/src/codegen_dot.ts +3 -3
  19. package/src/codegen_proof.ts +24 -2
  20. package/src/codegen_ts.ts +32 -19
  21. package/src/compile_package_main.ts +68 -11
  22. package/src/derived.ts +17 -0
  23. package/src/directive.ts +65 -0
  24. package/src/engine_entry.ts +22 -2
  25. package/src/enum_type.ts +71 -0
  26. package/src/fields.ts +135 -5
  27. package/src/flow_gate.ts +2 -1
  28. package/src/framework/workspaces.ts +73 -0
  29. package/src/idof_lint.ts +5 -1
  30. package/src/index.ts +20 -0
  31. package/src/kernel_capabilities.ts +12 -1
  32. package/src/law_check.ts +107 -28
  33. package/src/law_graph.ts +151 -20
  34. package/src/law_graph_usda.ts +23 -0
  35. package/src/law_value_type.ts +10 -3
  36. package/src/lsp_server.ts +26 -16
  37. package/src/package_config.ts +112 -9
  38. package/src/package_feature_topology.ts +174 -0
  39. package/src/plan_source_slice.ts +6 -0
  40. package/src/report.ts +6 -0
  41. package/src/stable_ids_for_module.ts +1 -0
  42. package/src/value_object.ts +9 -2
  43. package/src/workspace_sharding.ts +4 -4
package/src/aggregate.ts CHANGED
@@ -196,6 +196,8 @@ export interface AggregateHandle<
196
196
  readonly hasMany: HasManyFields<F>;
197
197
  /** Brand so a plain string is never accepted where a handle is required. */
198
198
  readonly __isAggregateHandle: true;
199
+ /** Name-only dependency handle. Export-safe, but never itself discoverable as domain law. */
200
+ readonly __isForwardRef?: true;
199
201
  /** DOMAIN CONTRACTS this aggregate `.implements(...)` — the stable public shapes it satisfies (compile-
200
202
  * checked). Codegen marks the aggregate as implementing them so cross-domain refs can target the CONTRACT
201
203
  * instead of this concrete type. Additive metadata (not hashed law). OMITTED when it implements none. */
@@ -730,5 +732,5 @@ export function instance<Id extends string, F extends Record<string, Field>>(
730
732
  * and every forward ref keeps compiling for free.
731
733
  */
732
734
  export function forwardRef<const Id extends string>(id: Id): AggregateHandle<Id, Record<string, never>> {
733
- return { id, fields: {}, hasMany: {}, __isAggregateHandle: true };
735
+ return { id, fields: {}, hasMany: {}, __isAggregateHandle: true, __isForwardRef: true };
734
736
  }
@@ -24,6 +24,7 @@
24
24
  * removed so tenant tooling cannot accidentally create a second law representation.
25
25
  */
26
26
  import { createHash } from "node:crypto";
27
+ import { z } from "zod";
27
28
 
28
29
  import type { AggregateHandle } from "./aggregate.js";
29
30
  import type { Directive } from "./directive.js";
@@ -33,7 +34,7 @@ import type { QueryDecl } from "./query.js";
33
34
  import type { SpatialDecl } from "./spatial.js";
34
35
  import type { AnySum } from "./sum.js";
35
36
  import type { AnyExtremum } from "./extremum.js";
36
- import type { DerivedDecl } from "./derived.js";
37
+ import type { AnyDerivedDecl, DerivedDecl, TemporalEnumDecl } from "./derived.js";
37
38
  import type { CombinedDecl } from "./combined.js";
38
39
  import type { ImpureCapabilityDecl } from "./codegen_provider_dart.js";
39
40
  import type { WorkspaceTypeDecl } from "./workspace_type.js";
@@ -68,7 +69,7 @@ import {
68
69
  type RelationTypeDecl,
69
70
  } from "./relation_schema.js";
70
71
  import { collectValueObjectDescriptors } from "./value_object.js";
71
- import { expandCapabilityExports } from "./capability_exports.js";
72
+ import { expandCapabilityExports, isCapabilityDecl } from "./capability_exports.js";
72
73
  import { RelationTuple, relationTuplesByObject } from "./framework/relations.js";
73
74
  import { signerDelegationByUser } from "./framework/signing_identity.js";
74
75
  import { AuthProvider, setAuthProvider, authProvider } from "./framework/workspaces.js";
@@ -111,7 +112,8 @@ export function aggregatesOf(mod: Mod): AggregateHandle[] {
111
112
  if (
112
113
  v &&
113
114
  typeof v === "object" &&
114
- (v as { __isAggregateHandle?: boolean }).__isAggregateHandle === true
115
+ (v as { __isAggregateHandle?: boolean }).__isAggregateHandle === true &&
116
+ (v as { __isForwardRef?: boolean }).__isForwardRef !== true
115
117
  ) {
116
118
  out.push(v as AggregateHandle);
117
119
  }
@@ -215,6 +217,17 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
215
217
  // `capability()` bundles expand into discoverable entries (the package plane's
216
218
  // merge point) — the quartet validation below then passes without re-exports.
217
219
  merged = expandCapabilityExports(merged);
220
+ const capabilityByAggregate = new Map<string, ImpureCapabilityDecl>();
221
+ for (const value of Object.values(merged)) {
222
+ if (isCapabilityDecl(value)) {
223
+ const capability = value as unknown as ImpureCapabilityDecl;
224
+ capabilityByAggregate.set(capability.aggregate.id, capability);
225
+ }
226
+ }
227
+ for (const capability of spec.impureCapabilities ?? []) {
228
+ capabilityByAggregate.set(capability.aggregate.id, capability);
229
+ }
230
+ const declaredCapabilities = [...capabilityByAggregate.values()];
218
231
  // METERED BIRTH: inject the sanctioning `nomosBirthRecordEnsure` directive + meter aggregates
219
232
  // into a `.births()`-declaring domain at the SAME seam the engine merge applies it — the
220
233
  // Ensure→NomosBirthRecord manifest declaration is what admits the auto-appended record through
@@ -272,6 +285,27 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
272
285
  : executableDirectivesForLanguage(applicationLanguage, discoveredDirectives, domain),
273
286
  conflictPolicies,
274
287
  );
288
+ // A business intent may direct a capability obligation as one private,
289
+ // atomic consequence. Language closure deliberately removes private effect
290
+ // directives from the application write surface; capability lifecycle law is
291
+ // different: the task declaration must retain its complete quartet so the
292
+ // kernel can seal the obligation and later judge provider receipts. Re-seat
293
+ // those exact framework-derived directives after closure. Generated tenant
294
+ // clients still expose only the declared language intents.
295
+ for (const capability of declaredCapabilities) {
296
+ const lifecycleDirectives = [
297
+ capability.obligation,
298
+ capability.complete,
299
+ capability.fail,
300
+ capability.block,
301
+ capability.deadLetter,
302
+ ] as unknown as Directive<any>[];
303
+ for (const directive of lifecycleDirectives) {
304
+ if (!directives.some((candidate) => candidate.id === directive.id)) {
305
+ directives.push(directive);
306
+ }
307
+ }
308
+ }
275
309
  // Invitations are ordinary, replayed law hidden behind the generated application. Each sharing
276
310
  // policy contributes one possession-proof redemption directive; it is injected after the tenant
277
311
  // language has been closed so it cannot become a second public lane.
@@ -492,7 +526,7 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
492
526
  domain,
493
527
  );
494
528
  const directiveIds = new Set(directives.map((d) => d.id));
495
- for (const task of spec.impureCapabilities ?? []) {
529
+ for (const task of declaredCapabilities) {
496
530
  for (const directive of [task.obligation, task.complete, task.fail, task.block, task.deadLetter]) {
497
531
  if (!directiveIds.has(directive.id)) {
498
532
  throw new Error(
@@ -516,9 +550,46 @@ export function composeDomainModule(spec: DomainModuleSpec): DomainModule {
516
550
  }
517
551
  const counts = [...(spec.counts ?? [])];
518
552
  const spatials = [...(spec.spatials ?? [])];
519
- const deriveds = [...(spec.deriveds ?? [])];
553
+ const temporalEnums: TemporalEnumDecl[] = aggregates.flatMap((aggregate) =>
554
+ Object.entries(aggregate.fields).flatMap(([sourceField, field]) =>
555
+ (field.temporalEnums ?? []).map((declaration) => {
556
+ if (field.isTimestamp !== true) {
557
+ throw new Error(
558
+ `domain '${domain}' temporal enum '${declaration.id}' targets ${aggregate.id}.${sourceField}, ` +
559
+ "which is not a t.timestamp() field",
560
+ );
561
+ }
562
+ const values = [
563
+ declaration.missing,
564
+ declaration.initial,
565
+ ...declaration.transitions.map((transition) => transition.value),
566
+ ] as [string, ...string[]];
567
+ return {
568
+ kind: "temporalEnum" as const,
569
+ id: declaration.id,
570
+ of: aggregate.id,
571
+ sourceField,
572
+ missing: declaration.missing,
573
+ initial: declaration.initial,
574
+ transitions: declaration.transitions,
575
+ returns: z.enum(values),
576
+ };
577
+ }),
578
+ ),
579
+ );
580
+ const derivedById = new Map<string, AnyDerivedDecl>();
581
+ for (const declaration of [...(spec.deriveds ?? []), ...temporalEnums]) {
582
+ const existing = derivedById.get(declaration.id);
583
+ if (existing !== undefined) {
584
+ throw new Error(
585
+ `domain '${domain}' declares read '${declaration.id}' more than once; temporal and generic derived reads share one namespace`,
586
+ );
587
+ }
588
+ derivedById.set(declaration.id, declaration);
589
+ }
590
+ const deriveds = [...derivedById.values()];
520
591
  const combineds = [...(spec.combineds ?? [])];
521
- const impureCapabilities = [...(spec.impureCapabilities ?? [])];
592
+ const impureCapabilities = declaredCapabilities;
522
593
  const sums = [...(spec.sums ?? [])];
523
594
  const mins = [...(spec.mins ?? [])];
524
595
  const maxes = [...(spec.maxes ?? [])];
@@ -16,6 +16,7 @@ import { pathToFileURL } from "node:url";
16
16
 
17
17
  import { composeDomainModule, type Mod } from "./build_package.js";
18
18
  import type { DomainModule } from "./codegen_dart.js";
19
+ import { bindEnumTypeExports } from "./enum_type.js";
19
20
  import { checkDomainModules, formatFindings, hasRefusal, type Finding } from "./law_check.js";
20
21
  import { LawSourceMap } from "./law_source_map.js";
21
22
  import {
@@ -29,17 +30,18 @@ import {
29
30
  import { parseStableIdLockfile } from "./stable_id_lockfile.js";
30
31
  import {
31
32
  validateNomosPackageConfig,
32
- type NomosPackageConfig,
33
+ type NormalizedNomosPackageConfig,
34
+ type NormalizedNomosPackageDomainConfig,
33
35
  } from "./package_config.js";
34
36
  import { assertPublicPackageImports } from "./package_source_imports.js";
37
+ import {
38
+ assertPackageFeatureTopology,
39
+ packageFeatureTopologyFindings,
40
+ } from "./package_feature_topology.js";
35
41
  import { packageDependencyUsageFindings } from "./package_dependency_usage.js";
36
42
 
37
- interface DomainConfig {
38
- key: string;
39
- name?: string;
40
- modules: string[];
41
- }
42
- interface PackageConfig extends NomosPackageConfig {
43
+ interface DomainConfig extends NormalizedNomosPackageDomainConfig {}
44
+ interface PackageConfig extends NormalizedNomosPackageConfig {
43
45
  readonly domains: readonly DomainConfig[];
44
46
  }
45
47
 
@@ -87,6 +89,15 @@ export async function loadComposedModulesWithSources(
87
89
  }
88
90
  const composed: DomainModule[] = [];
89
91
  const domainFiles = new Map<string, string[]>();
92
+ let sourceTopologyFindings: readonly Finding[] = [];
93
+ try {
94
+ sourceTopologyFindings = packageFeatureTopologyFindings(
95
+ assertPackageFeatureTopology(cfg.domains, cfgDir),
96
+ cfgDir,
97
+ );
98
+ } catch (error) {
99
+ fail(`package feature topology refused: ${(error as Error).message}`);
100
+ }
90
101
  for (const d of cfg.domains) {
91
102
  const mods: Mod[] = [];
92
103
  const files: string[] = [];
@@ -102,7 +113,9 @@ export async function loadComposedModulesWithSources(
102
113
  }
103
114
  for (const abs of files) {
104
115
  // Cache-bust so a re-import (the LSP re-check after an edit) re-evaluates the edited module.
105
- mods.push((await import(`${pathToFileURL(abs).href}?t=${Date.now()}`)) as Mod);
116
+ const mod = (await import(`${pathToFileURL(abs).href}?t=${Date.now()}`)) as Mod;
117
+ bindEnumTypeExports(mod, abs);
118
+ mods.push(mod);
106
119
  }
107
120
  domainFiles.set(d.key, files);
108
121
  composed.push(composeDomainModule({ name: d.name ?? d.key, domain: d.key, modules: mods }));
@@ -110,12 +123,15 @@ export async function loadComposedModulesWithSources(
110
123
  return {
111
124
  modules: composed,
112
125
  domainFiles,
113
- dependencyFindings: packageDependencyUsageFindings({
114
- packageName: cfg.name,
115
- exportedDomains: cfg.domains.map((domain) => domain.key),
116
- runtimeDependencies,
117
- modules: composed,
118
- }),
126
+ dependencyFindings: [
127
+ ...sourceTopologyFindings,
128
+ ...packageDependencyUsageFindings({
129
+ packageName: cfg.name,
130
+ exportedDomains: cfg.domains.map((domain) => domain.key),
131
+ runtimeDependencies,
132
+ modules: composed,
133
+ }),
134
+ ],
119
135
  };
120
136
  }
121
137
 
@@ -240,14 +256,21 @@ async function main(): Promise<void> {
240
256
  if (asJson) {
241
257
  const srcMap = new LawSourceMap(domainFiles);
242
258
  const out = findings.map((f) => {
243
- const loc = srcMap.locate(f.domain, f.directiveId);
259
+ const loc = f.source ?? srcMap.locate(f.domain, f.directiveId);
244
260
  return {
245
261
  domain: f.domain,
246
262
  ...(f.directiveId !== undefined ? { directiveId: f.directiveId } : {}),
247
263
  level: f.level,
248
264
  what: f.what,
249
265
  fix: f.fix,
250
- ...(loc !== undefined ? { file: loc.file, line: loc.line, column: loc.column, fallback: loc.fallback } : {}),
266
+ ...(loc !== undefined
267
+ ? {
268
+ file: loc.file,
269
+ line: loc.line,
270
+ column: loc.column,
271
+ ...("fallback" in loc ? { fallback: loc.fallback } : {}),
272
+ }
273
+ : {}),
251
274
  };
252
275
  });
253
276
  process.stdout.write(JSON.stringify(out) + "\n");