@cynodia/axiom-runtime 0.8.2-alpha.1 → 0.9.0-alpha.2
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/dom.d.ts +10 -0
- package/dist/runtime.d.ts +17 -0
- package/dist/runtime.js +78 -4
- package/package.json +2 -2
package/dist/dom.d.ts
CHANGED
|
@@ -86,5 +86,15 @@ export interface HostEnvironment {
|
|
|
86
86
|
queryIntegration?(operationId: string, args: Record<string, unknown>, options: {
|
|
87
87
|
timeoutMs?: number;
|
|
88
88
|
}): Promise<IntegrationQueryOutcome>;
|
|
89
|
+
/**
|
|
90
|
+
* Reads a stored object's metadata by key and returns it as a `BlobRef` record.
|
|
91
|
+
*
|
|
92
|
+
* The same shape as `queryIntegration`, and for the same reason: a `blob-metadata`
|
|
93
|
+
* operation is a finite question resolved before the transaction opens. It returns the
|
|
94
|
+
* reference, never the bytes — nothing in the runtime ever holds an object's contents.
|
|
95
|
+
* Only the authoritative runtime calls this; object stores, like integrations, are
|
|
96
|
+
* server-only.
|
|
97
|
+
*/
|
|
98
|
+
readBlobMetadata?(storageId: string, key: string): Promise<IntegrationQueryOutcome>;
|
|
89
99
|
}
|
|
90
100
|
//# sourceMappingURL=dom.d.ts.map
|
package/dist/runtime.d.ts
CHANGED
|
@@ -44,6 +44,10 @@ export declare const RUNTIME_DIAGNOSTIC_CODES: {
|
|
|
44
44
|
readonly INTEGRATION_RESULT_INVALID: "INTEGRATION_RESULT_INVALID";
|
|
45
45
|
/** An integration query failed. Never carries a provider secret. */
|
|
46
46
|
readonly INTEGRATION_QUERY_FAILED: "INTEGRATION_QUERY_FAILED";
|
|
47
|
+
/** No host capable of reaching an object store is configured. */
|
|
48
|
+
readonly BLOB_STORAGE_UNAVAILABLE: "BLOB_STORAGE_UNAVAILABLE";
|
|
49
|
+
/** A `blob-metadata` lookup failed: no such key, a staged object, or the store refused. */
|
|
50
|
+
readonly BLOB_METADATA_FAILED: "BLOB_METADATA_FAILED";
|
|
47
51
|
};
|
|
48
52
|
export type RuntimeDiagnosticCode = (typeof RUNTIME_DIAGNOSTIC_CODES)[keyof typeof RUNTIME_DIAGNOSTIC_CODES];
|
|
49
53
|
/**
|
|
@@ -81,6 +85,19 @@ export interface EffectIntentRecord {
|
|
|
81
85
|
succeededEventId?: NodeId;
|
|
82
86
|
failedEventId?: NodeId;
|
|
83
87
|
outcome?: 'committed' | 'rolled-back';
|
|
88
|
+
/**
|
|
89
|
+
* Set instead of an integration operation when the intent is a storage effect — a
|
|
90
|
+
* `blob-commit` or a `blob-delete`. It rides the same outbox for the same reason: an
|
|
91
|
+
* object store cannot join an Axiom transaction, so the intent commits with the state
|
|
92
|
+
* that references the object and dispatches only afterwards. `operationId` then carries
|
|
93
|
+
* the store's id, so one log and one dispatcher serve both kinds of effect rather than
|
|
94
|
+
* a second, parallel durability system (spec 0.9 §57).
|
|
95
|
+
*/
|
|
96
|
+
storage?: {
|
|
97
|
+
storageId: NodeId;
|
|
98
|
+
operation: 'commit' | 'delete';
|
|
99
|
+
key: string;
|
|
100
|
+
};
|
|
84
101
|
}
|
|
85
102
|
export interface ActionResult {
|
|
86
103
|
ok: boolean;
|
package/dist/runtime.js
CHANGED
|
@@ -48,6 +48,10 @@ export const RUNTIME_DIAGNOSTIC_CODES = {
|
|
|
48
48
|
INTEGRATION_RESULT_INVALID: 'INTEGRATION_RESULT_INVALID',
|
|
49
49
|
/** An integration query failed. Never carries a provider secret. */
|
|
50
50
|
INTEGRATION_QUERY_FAILED: 'INTEGRATION_QUERY_FAILED',
|
|
51
|
+
/** No host capable of reaching an object store is configured. */
|
|
52
|
+
BLOB_STORAGE_UNAVAILABLE: 'BLOB_STORAGE_UNAVAILABLE',
|
|
53
|
+
/** A `blob-metadata` lookup failed: no such key, a staged object, or the store refused. */
|
|
54
|
+
BLOB_METADATA_FAILED: 'BLOB_METADATA_FAILED',
|
|
51
55
|
};
|
|
52
56
|
const MISSING = Symbol('missing');
|
|
53
57
|
function unwrapType(type) {
|
|
@@ -1083,6 +1087,7 @@ export function createAxiomRuntime(options) {
|
|
|
1083
1087
|
return;
|
|
1084
1088
|
}
|
|
1085
1089
|
case 'integration-query':
|
|
1090
|
+
case 'blob-metadata':
|
|
1086
1091
|
// The result was already resolved and bound into the action's scope before this
|
|
1087
1092
|
// transaction opened — see `runActionAsync`. Nothing to do here; this case exists
|
|
1088
1093
|
// only so the kind is recognized rather than falling to `default`.
|
|
@@ -1109,6 +1114,27 @@ export function createAxiomRuntime(options) {
|
|
|
1109
1114
|
});
|
|
1110
1115
|
return;
|
|
1111
1116
|
}
|
|
1117
|
+
case 'blob-commit':
|
|
1118
|
+
case 'blob-delete': {
|
|
1119
|
+
// Identical discipline to `integration-effect`: no store is called here. Reaching
|
|
1120
|
+
// the operation records intent in the same log, discarded on rollback the same way,
|
|
1121
|
+
// and dispatched only once the surrounding transaction has committed.
|
|
1122
|
+
effectIntentLog.push({
|
|
1123
|
+
id: host.uuid(),
|
|
1124
|
+
transactionId: context.transactionId,
|
|
1125
|
+
...(context.sourceNodeId ? { actionId: context.sourceNodeId } : {}),
|
|
1126
|
+
operationId: operation.storageId,
|
|
1127
|
+
arguments: {},
|
|
1128
|
+
storage: {
|
|
1129
|
+
storageId: operation.storageId,
|
|
1130
|
+
operation: operation.kind === 'blob-commit' ? 'commit' : 'delete',
|
|
1131
|
+
key: toText(evaluate(operation.blobKey, scope)),
|
|
1132
|
+
},
|
|
1133
|
+
...(operation.succeededEventId ? { succeededEventId: operation.succeededEventId } : {}),
|
|
1134
|
+
...(operation.failedEventId ? { failedEventId: operation.failedEventId } : {}),
|
|
1135
|
+
});
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1112
1138
|
default:
|
|
1113
1139
|
result.push({
|
|
1114
1140
|
code: RUNTIME_DIAGNOSTIC_CODES.UNSUPPORTED_OPERATION,
|
|
@@ -1267,8 +1293,8 @@ export function createAxiomRuntime(options) {
|
|
|
1267
1293
|
return result;
|
|
1268
1294
|
});
|
|
1269
1295
|
}
|
|
1270
|
-
function
|
|
1271
|
-
return (action.operations ?? []).some((operation) => operation.kind === 'integration-query');
|
|
1296
|
+
function actionHasAsyncQuery(action) {
|
|
1297
|
+
return (action.operations ?? []).some((operation) => operation.kind === 'integration-query' || operation.kind === 'blob-metadata');
|
|
1272
1298
|
}
|
|
1273
1299
|
/**
|
|
1274
1300
|
* Resolves every top-level `integration-query` operation before the transaction opens,
|
|
@@ -1282,7 +1308,7 @@ export function createAxiomRuntime(options) {
|
|
|
1282
1308
|
*/
|
|
1283
1309
|
async function runActionAsync(actionId, args = {}) {
|
|
1284
1310
|
const action = ir.actions[actionId];
|
|
1285
|
-
if (!action || !
|
|
1311
|
+
if (!action || !actionHasAsyncQuery(action) || remoteActionIds.has(actionId)) {
|
|
1286
1312
|
return runAction(actionId, args);
|
|
1287
1313
|
}
|
|
1288
1314
|
const scope = rootScope();
|
|
@@ -1290,7 +1316,55 @@ export function createAxiomRuntime(options) {
|
|
|
1290
1316
|
scope.values.set(parameter.id, args[parameter.id] ?? null);
|
|
1291
1317
|
}
|
|
1292
1318
|
const presetBindings = new Map();
|
|
1319
|
+
const failWith = (diagnostic) => {
|
|
1320
|
+
report(diagnostic);
|
|
1321
|
+
recordOutcome(actionId, 'failed', [diagnostic]);
|
|
1322
|
+
renderApplication();
|
|
1323
|
+
return { ok: false, diagnostics: [diagnostic] };
|
|
1324
|
+
};
|
|
1293
1325
|
for (const operation of action.operations ?? []) {
|
|
1326
|
+
if (operation.kind === 'blob-metadata') {
|
|
1327
|
+
// A metadata lookup is the storage half of the same pre-transaction phase: a finite
|
|
1328
|
+
// question, answered before anything is mutated, whose result later operations read.
|
|
1329
|
+
const key = toText(evaluate(operation.blobKey, scope));
|
|
1330
|
+
let blobOutcome;
|
|
1331
|
+
try {
|
|
1332
|
+
blobOutcome = await host.readBlobMetadata?.(String(operation.storageId), key);
|
|
1333
|
+
}
|
|
1334
|
+
catch (error) {
|
|
1335
|
+
blobOutcome = {
|
|
1336
|
+
ok: false,
|
|
1337
|
+
code: RUNTIME_DIAGNOSTIC_CODES.BLOB_METADATA_FAILED,
|
|
1338
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
1341
|
+
if (!blobOutcome) {
|
|
1342
|
+
return failWith({
|
|
1343
|
+
code: RUNTIME_DIAGNOSTIC_CODES.BLOB_STORAGE_UNAVAILABLE,
|
|
1344
|
+
message: `${action.name ?? action.id} reads object storage, but this host provides none`,
|
|
1345
|
+
severity: 'error',
|
|
1346
|
+
nodeId: action.id,
|
|
1347
|
+
actionId: action.id,
|
|
1348
|
+
details: { storageId: String(operation.storageId) },
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
if (!blobOutcome.ok) {
|
|
1352
|
+
// The store's own code travels in `details.code` rather than replacing the
|
|
1353
|
+
// diagnostic code: a provider's vocabulary is not Axiom's, and a caller matching
|
|
1354
|
+
// on `code` must see a code this runtime actually declares.
|
|
1355
|
+
return failWith({
|
|
1356
|
+
code: RUNTIME_DIAGNOSTIC_CODES.BLOB_METADATA_FAILED,
|
|
1357
|
+
message: blobOutcome.message,
|
|
1358
|
+
severity: 'error',
|
|
1359
|
+
nodeId: action.id,
|
|
1360
|
+
actionId: action.id,
|
|
1361
|
+
details: { storageId: String(operation.storageId), code: blobOutcome.code },
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
scope.values.set(operation.bindAs, blobOutcome.value);
|
|
1365
|
+
presetBindings.set(operation.bindAs, blobOutcome.value);
|
|
1366
|
+
continue;
|
|
1367
|
+
}
|
|
1294
1368
|
if (operation.kind !== 'integration-query') {
|
|
1295
1369
|
continue;
|
|
1296
1370
|
}
|
|
@@ -2455,7 +2529,7 @@ export function createAxiomRuntime(options) {
|
|
|
2455
2529
|
},
|
|
2456
2530
|
async invokeActionAsync(id, args = {}) {
|
|
2457
2531
|
const action = ir.actions[id];
|
|
2458
|
-
if (action &&
|
|
2532
|
+
if (action && actionHasAsyncQuery(action) && !remoteActionIds.has(id)) {
|
|
2459
2533
|
return runActionAsync(id, args);
|
|
2460
2534
|
}
|
|
2461
2535
|
const result = runAction(id, args);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cynodia/axiom-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0-alpha.2",
|
|
4
4
|
"description": "Domain-independent runtime that executes an Axiom application graph.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "AskTech AS",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
}
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@cynodia/axiom-core": "0.
|
|
34
|
+
"@cynodia/axiom-core": "0.9.0-alpha.2"
|
|
35
35
|
},
|
|
36
36
|
"scripts": {
|
|
37
37
|
"build": "tsc -b tsconfig.json tsconfig.test.json",
|