@cynodia/axiom-core 0.11.2-alpha.1 → 0.12.0-alpha.1

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/dist/graph.js CHANGED
@@ -23,7 +23,7 @@ export class ApplicationGraph {
23
23
  /** Bumped by every change, so the derived edge index can never serve stale data. */
24
24
  revision = 0;
25
25
  semanticIndex;
26
- constructor(id, name, version = '0.11.2') {
26
+ constructor(id, name, version = '0.12.0') {
27
27
  this.data = { id, name, version, nodes: {}, edges: {} };
28
28
  }
29
29
  get id() {
package/dist/index.d.ts CHANGED
@@ -24,6 +24,7 @@ export * from './ui.js';
24
24
  export * from './types.js';
25
25
  export * from './graph.js';
26
26
  export * from './schema-identity.js';
27
+ export * from './semantic-identity.js';
27
28
  export * from './schema-diff.js';
28
29
  export * from './infer.js';
29
30
  export * from './context.js';
package/dist/index.js CHANGED
@@ -24,6 +24,7 @@ export * from './ui.js';
24
24
  export * from './types.js';
25
25
  export * from './graph.js';
26
26
  export * from './schema-identity.js';
27
+ export * from './semantic-identity.js';
27
28
  export * from './schema-diff.js';
28
29
  export * from './infer.js';
29
30
  export * from './context.js';
@@ -0,0 +1,95 @@
1
+ import type { ApplicationGraph } from './graph.js';
2
+ /**
3
+ * Application **semantic** identity (spec12 §45, §46).
4
+ *
5
+ * `schemaFingerprint` (spec11) deliberately covers only *persistence-relevant* structure —
6
+ * entity fields, types, `required`, identity fields, state shapes, relationships, which
7
+ * entity a read policy governs — and deliberately excludes executable meaning: two graphs
8
+ * whose actions do completely different things, but store the same shapes, fingerprint
9
+ * identically. That is correct for deciding whether *data* needs migrating; it is not enough
10
+ * to decide whether two authority processes may safely execute the **same durable work**.
11
+ *
12
+ * `semanticFingerprint` is the second identity: a deterministic hash over the executable
13
+ * server-side meaning of a graph.
14
+ *
15
+ * ### Inclusions (what changes the semantic fingerprint)
16
+ *
17
+ * - `ActionDef` — parameters (id + type + required), guards / preconditions / failure modes,
18
+ * the full `operations` tree, postconditions, `authorization`, `invocation.allowedSources`,
19
+ * `destructive`, `requiresConfirmation`.
20
+ * - `IntegrationDef` — capability domain; `IntegrationOperationDef` — `mode`, `resultType`,
21
+ * `parameters`, `retry`, `idempotent`, `timeoutMs`, `idempotencyKey`, `succeededEventId`,
22
+ * `failedEventId`, `clientSafe`.
23
+ * - `TriggerDef` — `when`, `actionId`, `arguments`, `enabledWhen`, overlap policy,
24
+ * `invocationSource`.
25
+ * - `EventDef` — `payloadType`.
26
+ * - `SubscriptionDef` — `integrationId`, `eventId`, delivery / ordering / backpressure /
27
+ * reconnect policy, `deduplicateBy`, lifecycle.
28
+ * - `ReadPolicyDef` — `entityId` and the `predicate` expression.
29
+ * - `QueryDef` — every clause expression, ordering, pagination shape.
30
+ * - `ExpressionDef` — `parameters` and the body `expression`.
31
+ * - `ConstraintDef` / `TransitionConstraintDef` — `entityId`, `severity`, the expression(s),
32
+ * the bound scope ids.
33
+ * - `StorageDef` — `readAuthorization` / `uploadAuthorization` expressions, `retry`.
34
+ * - `RelationshipDef` — endpoints and cardinality (also in the schema fingerprint; repeated
35
+ * here so a semantic-only comparison is self-contained).
36
+ * - `graph.schemaVersion`.
37
+ *
38
+ * ### Exclusions (what does NOT change it)
39
+ *
40
+ * - Every UI node kind, `RouteDef`, themes, presentation, `headingLevel`, icons.
41
+ * - `name`, `description`, `label` anywhere in the tree — reasoning is by stable id.
42
+ * - Anything under {@link AUTHORING_METADATA_KEY}, and free-form `metadata` (an annotation
43
+ * channel, not executable vocabulary — same treatment the schema fingerprint gives names).
44
+ * - Declaration order — every collection is sorted by `id`.
45
+ *
46
+ * The projection is portable plain data and the algorithm is versioned
47
+ * ({@link SEMANTIC_FINGERPRINT_VERSION}); a future non-JS runtime reconstructs it from the
48
+ * Server IR and hashes the same bytes (spec12 §95, §96).
49
+ */
50
+ /** The projection algorithm's own version, mixed into the hash (spec12 §46). */
51
+ export declare const SEMANTIC_FINGERPRINT_VERSION = 1;
52
+ export interface SemanticProjection {
53
+ fingerprintVersion: number;
54
+ schemaVersion: number;
55
+ /** Executable nodes, grouped by kind, each group sorted by id, each node stripped of human metadata. */
56
+ nodes: Record<string, unknown[]>;
57
+ }
58
+ type SemanticInput = ApplicationGraph;
59
+ /** Build the canonical executable-meaning projection of a graph (spec12 §46). */
60
+ export declare function semanticProjection(graph: SemanticInput): SemanticProjection;
61
+ /** SHA-256 (hex) of a graph's executable-meaning projection (spec12 §46). Pure, synchronous. */
62
+ export declare function semanticFingerprint(graph: SemanticInput): string;
63
+ /**
64
+ * The durable runtime compatibility identity of one authority build (spec12 §45). Two
65
+ * authorities may safely execute the same durable work iff their keys are equal.
66
+ *
67
+ * - `schemaVersion` / `schemaFingerprint` — the 0.11 identity, unchanged; a schema mismatch
68
+ * is already fatal per 0.11 migration safety (spec12 §42).
69
+ * - `serverContract` — the Server IR contract the graph requires (`axiom.server.v7`, …); a
70
+ * runtime that speaks a different one cannot be assumed to execute the vocabulary.
71
+ * - `semanticFingerprint` — executable meaning; catches "same schema, different action
72
+ * bodies" (spec12 §44), which the schema fingerprint cannot.
73
+ */
74
+ export interface AuthorityCompatibilityKey {
75
+ schemaVersion: number;
76
+ schemaFingerprint: string;
77
+ serverContract: string;
78
+ semanticFingerprint: string;
79
+ }
80
+ export declare function authorityCompatibilityKey(parts: AuthorityCompatibilityKey): AuthorityCompatibilityKey;
81
+ /** A stable, comparable string form for storing on a durable work item (spec12 §43). */
82
+ export declare function compatibilityKeyString(key: AuthorityCompatibilityKey): string;
83
+ export interface CompatibilityComparison {
84
+ compatible: boolean;
85
+ /** Field names that differ, when not compatible — for an `INCOMPATIBLE_AUTHORITY` diagnostic. */
86
+ mismatches: Array<keyof AuthorityCompatibilityKey>;
87
+ }
88
+ /**
89
+ * Compare two authority compatibility keys. Fails **closed**: any difference in any field
90
+ * makes them incompatible (spec12 §44 "fail closed when semantic compatibility cannot be
91
+ * established"). There is no partial compatibility.
92
+ */
93
+ export declare function compareAuthorityCompatibility(a: AuthorityCompatibilityKey, b: AuthorityCompatibilityKey): CompatibilityComparison;
94
+ export {};
95
+ //# sourceMappingURL=semantic-identity.d.ts.map
@@ -0,0 +1,136 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { canonicalJSON } from './schema-identity.js';
3
+ import { AUTHORING_METADATA_KEY } from './authoring-metadata.js';
4
+ /**
5
+ * Application **semantic** identity (spec12 §45, §46).
6
+ *
7
+ * `schemaFingerprint` (spec11) deliberately covers only *persistence-relevant* structure —
8
+ * entity fields, types, `required`, identity fields, state shapes, relationships, which
9
+ * entity a read policy governs — and deliberately excludes executable meaning: two graphs
10
+ * whose actions do completely different things, but store the same shapes, fingerprint
11
+ * identically. That is correct for deciding whether *data* needs migrating; it is not enough
12
+ * to decide whether two authority processes may safely execute the **same durable work**.
13
+ *
14
+ * `semanticFingerprint` is the second identity: a deterministic hash over the executable
15
+ * server-side meaning of a graph.
16
+ *
17
+ * ### Inclusions (what changes the semantic fingerprint)
18
+ *
19
+ * - `ActionDef` — parameters (id + type + required), guards / preconditions / failure modes,
20
+ * the full `operations` tree, postconditions, `authorization`, `invocation.allowedSources`,
21
+ * `destructive`, `requiresConfirmation`.
22
+ * - `IntegrationDef` — capability domain; `IntegrationOperationDef` — `mode`, `resultType`,
23
+ * `parameters`, `retry`, `idempotent`, `timeoutMs`, `idempotencyKey`, `succeededEventId`,
24
+ * `failedEventId`, `clientSafe`.
25
+ * - `TriggerDef` — `when`, `actionId`, `arguments`, `enabledWhen`, overlap policy,
26
+ * `invocationSource`.
27
+ * - `EventDef` — `payloadType`.
28
+ * - `SubscriptionDef` — `integrationId`, `eventId`, delivery / ordering / backpressure /
29
+ * reconnect policy, `deduplicateBy`, lifecycle.
30
+ * - `ReadPolicyDef` — `entityId` and the `predicate` expression.
31
+ * - `QueryDef` — every clause expression, ordering, pagination shape.
32
+ * - `ExpressionDef` — `parameters` and the body `expression`.
33
+ * - `ConstraintDef` / `TransitionConstraintDef` — `entityId`, `severity`, the expression(s),
34
+ * the bound scope ids.
35
+ * - `StorageDef` — `readAuthorization` / `uploadAuthorization` expressions, `retry`.
36
+ * - `RelationshipDef` — endpoints and cardinality (also in the schema fingerprint; repeated
37
+ * here so a semantic-only comparison is self-contained).
38
+ * - `graph.schemaVersion`.
39
+ *
40
+ * ### Exclusions (what does NOT change it)
41
+ *
42
+ * - Every UI node kind, `RouteDef`, themes, presentation, `headingLevel`, icons.
43
+ * - `name`, `description`, `label` anywhere in the tree — reasoning is by stable id.
44
+ * - Anything under {@link AUTHORING_METADATA_KEY}, and free-form `metadata` (an annotation
45
+ * channel, not executable vocabulary — same treatment the schema fingerprint gives names).
46
+ * - Declaration order — every collection is sorted by `id`.
47
+ *
48
+ * The projection is portable plain data and the algorithm is versioned
49
+ * ({@link SEMANTIC_FINGERPRINT_VERSION}); a future non-JS runtime reconstructs it from the
50
+ * Server IR and hashes the same bytes (spec12 §95, §96).
51
+ */
52
+ /** The projection algorithm's own version, mixed into the hash (spec12 §46). */
53
+ export const SEMANTIC_FINGERPRINT_VERSION = 1;
54
+ const EXECUTABLE_KINDS = [
55
+ 'action',
56
+ 'integration',
57
+ 'integration-operation',
58
+ 'trigger',
59
+ 'event',
60
+ 'subscription',
61
+ 'read-policy',
62
+ 'query',
63
+ 'expression',
64
+ 'constraint',
65
+ 'transition-constraint',
66
+ 'storage',
67
+ 'relationship',
68
+ ];
69
+ /** Keys removed everywhere in the tree — human metadata, never executable meaning. */
70
+ const NON_SEMANTIC_KEYS = new Set(['name', 'description', 'label', 'metadata', AUTHORING_METADATA_KEY]);
71
+ function stripNonSemantic(value) {
72
+ if (Array.isArray(value)) {
73
+ return value.map(stripNonSemantic);
74
+ }
75
+ if (value && typeof value === 'object') {
76
+ const out = {};
77
+ for (const [key, v] of Object.entries(value)) {
78
+ if (NON_SEMANTIC_KEYS.has(key) || v === undefined) {
79
+ continue;
80
+ }
81
+ out[key] = stripNonSemantic(v);
82
+ }
83
+ return out;
84
+ }
85
+ return value;
86
+ }
87
+ function byId(items) {
88
+ return [...items].sort((a, b) => (String(a.id) < String(b.id) ? -1 : String(a.id) > String(b.id) ? 1 : 0));
89
+ }
90
+ /** Build the canonical executable-meaning projection of a graph (spec12 §46). */
91
+ export function semanticProjection(graph) {
92
+ const nodes = {};
93
+ for (const kind of EXECUTABLE_KINDS) {
94
+ const group = graph.getNodesByKind(kind);
95
+ if (group.length === 0) {
96
+ continue;
97
+ }
98
+ nodes[kind] = byId(group).map((node) => stripNonSemantic(node));
99
+ }
100
+ return {
101
+ fingerprintVersion: SEMANTIC_FINGERPRINT_VERSION,
102
+ schemaVersion: graph.schemaVersion,
103
+ nodes,
104
+ };
105
+ }
106
+ /** SHA-256 (hex) of a graph's executable-meaning projection (spec12 §46). Pure, synchronous. */
107
+ export function semanticFingerprint(graph) {
108
+ return createHash('sha256').update(canonicalJSON(semanticProjection(graph))).digest('hex');
109
+ }
110
+ export function authorityCompatibilityKey(parts) {
111
+ return {
112
+ schemaVersion: parts.schemaVersion,
113
+ schemaFingerprint: parts.schemaFingerprint,
114
+ serverContract: parts.serverContract,
115
+ semanticFingerprint: parts.semanticFingerprint,
116
+ };
117
+ }
118
+ /** A stable, comparable string form for storing on a durable work item (spec12 §43). */
119
+ export function compatibilityKeyString(key) {
120
+ return canonicalJSON(authorityCompatibilityKey(key));
121
+ }
122
+ /**
123
+ * Compare two authority compatibility keys. Fails **closed**: any difference in any field
124
+ * makes them incompatible (spec12 §44 "fail closed when semantic compatibility cannot be
125
+ * established"). There is no partial compatibility.
126
+ */
127
+ export function compareAuthorityCompatibility(a, b) {
128
+ const fields = [
129
+ 'schemaVersion',
130
+ 'schemaFingerprint',
131
+ 'serverContract',
132
+ 'semanticFingerprint',
133
+ ];
134
+ const mismatches = fields.filter((field) => a[field] !== b[field]);
135
+ return { compatible: mismatches.length === 0, mismatches };
136
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cynodia/axiom-core",
3
- "version": "0.11.2-alpha.1",
3
+ "version": "0.12.0-alpha.1",
4
4
  "description": "Application Graph, semantic types, locations and validation for Axiom.",
5
5
  "license": "MIT",
6
6
  "author": "AskTech AS",