@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 +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/semantic-identity.d.ts +95 -0
- package/dist/semantic-identity.js +136 -0
- package/package.json +1 -1
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.
|
|
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
|
+
}
|