@cynodia/axiom-server 0.12.0-alpha.1 → 0.12.1-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/server.d.ts +18 -1
- package/dist/server.js +92 -4
- package/dist/sqlite-persistence.d.ts +18 -0
- package/dist/sqlite-persistence.js +84 -48
- package/package.json +3 -3
package/dist/server.d.ts
CHANGED
|
@@ -201,10 +201,25 @@ export interface AxiomServer {
|
|
|
201
201
|
start(): Promise<void>;
|
|
202
202
|
/** The one entry point. Every transport funnels through here. */
|
|
203
203
|
handle(request: ServerRequest): Promise<ServerResponse>;
|
|
204
|
-
/**
|
|
204
|
+
/**
|
|
205
|
+
* This authority's **local view** of every observable state — the state as of the most
|
|
206
|
+
* recently handled request (spec12.1 §6, §39, §40). It is a synchronous read of the
|
|
207
|
+
* authority-local cache and does not itself reconcile with persistence. In a multi-authority
|
|
208
|
+
* deployment, read authoritative state through `handle(SnapshotRequest)` or an action, both
|
|
209
|
+
* of which reconcile to the durable revision first; or call {@link coherentSnapshot}.
|
|
210
|
+
*/
|
|
205
211
|
snapshot(): StateSnapshot;
|
|
212
|
+
/** A single state's value from the authority-local view. See {@link snapshot} for the coherence contract. */
|
|
206
213
|
getState(id: NodeId): unknown;
|
|
214
|
+
/** The durable store revision this authority's local view is at (spec12.1 §22, monotonic). */
|
|
207
215
|
revision(): number;
|
|
216
|
+
/**
|
|
217
|
+
* Reconcile to the durable revision (reloading persisted state if another authority has
|
|
218
|
+
* committed) and return a revision-coherent snapshot (spec12.1 §7, §38, §40). This is the
|
|
219
|
+
* async counterpart of {@link snapshot}; `handle(SnapshotRequest)` performs the same
|
|
220
|
+
* reconciliation for the protocol path.
|
|
221
|
+
*/
|
|
222
|
+
coherentSnapshot(): Promise<StateSnapshot>;
|
|
208
223
|
/** Every mutation this authority has applied, with its outcome. */
|
|
209
224
|
mutationLog(): MutationLogEntry[];
|
|
210
225
|
/**
|
|
@@ -295,6 +310,8 @@ export interface AxiomServer {
|
|
|
295
310
|
export interface DistributedWorkInspection {
|
|
296
311
|
authority: AuthorityInfo;
|
|
297
312
|
effects: import('./durable-work.js').DurableWorkItemView[];
|
|
313
|
+
/** Logical scheduled firings this authority knows about, with their lease liveness (spec12.1 §53). */
|
|
314
|
+
schedules: import('./durable-work.js').DurableWorkItemView[];
|
|
298
315
|
incompatibleEffects: import('./durable-work.js').DurableWorkItem[];
|
|
299
316
|
}
|
|
300
317
|
export declare function createAxiomServer(options: AxiomServerOptions): AxiomServer;
|
package/dist/server.js
CHANGED
|
@@ -458,6 +458,54 @@ export function createAxiomServer(options) {
|
|
|
458
458
|
}
|
|
459
459
|
return { revision: storeRevision, states, ...(incremental ? { partial: true } : {}) };
|
|
460
460
|
}
|
|
461
|
+
/**
|
|
462
|
+
* Distributed state coherence (spec12.1 §6-§9, §11, §17-§22).
|
|
463
|
+
*
|
|
464
|
+
* The in-memory `StateDef` representation inside a running authority is an **authority-local
|
|
465
|
+
* cache** of persisted authoritative state — never an independent store. Before any
|
|
466
|
+
* operation whose semantic result depends on authoritative `StateDef` state, this
|
|
467
|
+
* re-observes the durable revision; if another authority has committed since, it reloads
|
|
468
|
+
* the persisted state so execution proceeds from a coherent revision.
|
|
469
|
+
*
|
|
470
|
+
* - The correctness mechanism is durable-revision observation, never a broadcast: a lost
|
|
471
|
+
* notification cannot leave this authority indefinitely stale (§17).
|
|
472
|
+
* - A refresh always corresponds to one coherent persisted revision — if the store moves
|
|
473
|
+
* while loading, it repeats (bounded) rather than mixing revisions (§19, §20).
|
|
474
|
+
* - `storeRevision` is monotonic: this never publishes local state at a revision below one
|
|
475
|
+
* already observed (§22).
|
|
476
|
+
*
|
|
477
|
+
* Everything that calls this runs inside the server's single serialized queue, so there is
|
|
478
|
+
* never a concurrent refresh within one authority.
|
|
479
|
+
*/
|
|
480
|
+
async function ensureStateCoherent() {
|
|
481
|
+
let observed = await persistence.revision();
|
|
482
|
+
if (observed <= storeRevision) {
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
486
|
+
const rows = await persistence.load();
|
|
487
|
+
const after = await persistence.revision();
|
|
488
|
+
if (after !== observed && attempt < 7) {
|
|
489
|
+
observed = after;
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
for (const row of rows) {
|
|
493
|
+
const state = statesById.get(row.stateId);
|
|
494
|
+
if (!state || state.derivation) {
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
runtime.hydrateState(row.stateId, row.value);
|
|
498
|
+
revisions.set(row.stateId, row.revision);
|
|
499
|
+
}
|
|
500
|
+
if (observed > storeRevision) {
|
|
501
|
+
storeRevision = observed;
|
|
502
|
+
}
|
|
503
|
+
// A reload may change any cached query result (a principal/policy-scoped entry
|
|
504
|
+
// included); the query cache already invalidates blindly on any commit (spec 0.10 §72).
|
|
505
|
+
invalidateQueryCache();
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
461
509
|
/**
|
|
462
510
|
* The key an idempotency record is filed under.
|
|
463
511
|
*
|
|
@@ -858,6 +906,12 @@ export function createAxiomServer(options) {
|
|
|
858
906
|
*/
|
|
859
907
|
async function invokeCore(actionId, args, context, requestId, replayKey, depth) {
|
|
860
908
|
const startedAt = Date.now();
|
|
909
|
+
// spec12.1 §9, §41-§44: an ActionDef transaction MUST begin from a StateDef snapshot
|
|
910
|
+
// corresponding to the persistence revision it will attempt to commit against — for a
|
|
911
|
+
// client request, a trigger, a scheduled firing, an event-invoked action or an
|
|
912
|
+
// effect-outcome action alike. Reconcile to the durable revision before any
|
|
913
|
+
// state-dependent semantics (guards, authorization, constraints, operations) run.
|
|
914
|
+
await ensureStateCoherent();
|
|
861
915
|
// The caller is bound for the whole invocation, not only for the authorization check.
|
|
862
916
|
// An operation that records who acted — `field(ref(PRINCIPAL), F_USER_ID)` — must resolve
|
|
863
917
|
// whether or not the action also happens to carry an authorization rule. It is also what
|
|
@@ -991,6 +1045,13 @@ export function createAxiomServer(options) {
|
|
|
991
1045
|
for (const stateId of writes) {
|
|
992
1046
|
runtime.hydrateState(stateId, before.get(stateId));
|
|
993
1047
|
}
|
|
1048
|
+
// spec12.1 §11, §14, §15: this authority lost an optimistic concurrency race. The
|
|
1049
|
+
// losing invocation returns CONCURRENCY_CONFLICT (no silent replay, §12) — but the
|
|
1050
|
+
// authority itself MUST recover: its local StateDef is now known invalid, so reload
|
|
1051
|
+
// the winning durable state before it processes any subsequent request. Without this,
|
|
1052
|
+
// the base revision stays stale and every future commit fails the same way (the F1
|
|
1053
|
+
// wedge).
|
|
1054
|
+
await ensureStateCoherent();
|
|
994
1055
|
const diagnostics = [
|
|
995
1056
|
diagnostic(SERVER_DIAGNOSTIC_CODES.CONCURRENCY_CONFLICT, `${action.name ?? action.id} was not committed: ${outcome.conflicts.join(', ')} changed while it ran`, { actionId: action.id, conflicts: outcome.conflicts }),
|
|
996
1057
|
];
|
|
@@ -1367,13 +1428,27 @@ export function createAxiomServer(options) {
|
|
|
1367
1428
|
const gate = schemaGateWithoutStore(ir);
|
|
1368
1429
|
throw new Error(`${gate.code}: ${gate.message}`);
|
|
1369
1430
|
}
|
|
1370
|
-
|
|
1431
|
+
// spec12.1 §37: publish a coherent (state, revision) pair. `persisted` above may be a
|
|
1432
|
+
// read behind by now (the migration gate did async work); re-load so the hydrated
|
|
1433
|
+
// state and `storeRevision` identify the same durable revision, repeating if a
|
|
1434
|
+
// concurrent authority commits during the load (§19, §20).
|
|
1435
|
+
let coherent;
|
|
1436
|
+
let coherentAt;
|
|
1437
|
+
for (let attempt = 0;; attempt += 1) {
|
|
1438
|
+
coherentAt = await persistence.revision();
|
|
1439
|
+
coherent = await persistence.load();
|
|
1440
|
+
const after = await persistence.revision();
|
|
1441
|
+
if (after === coherentAt || attempt >= 7) {
|
|
1442
|
+
break;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
for (const entry of coherent) {
|
|
1371
1446
|
if (statesById.has(entry.stateId)) {
|
|
1372
1447
|
runtime.hydrateState(entry.stateId, entry.value);
|
|
1373
1448
|
revisions.set(entry.stateId, entry.revision);
|
|
1374
1449
|
}
|
|
1375
1450
|
}
|
|
1376
|
-
storeRevision =
|
|
1451
|
+
storeRevision = coherentAt;
|
|
1377
1452
|
// A crash between an effect intent's commit and its dispatch must not lose it: every
|
|
1378
1453
|
// pending intent found on restart resumes dispatch here (spec §19,96,140).
|
|
1379
1454
|
const pending = (await persistence.loadPendingEffects?.()) ?? [];
|
|
@@ -1491,6 +1566,12 @@ export function createAxiomServer(options) {
|
|
|
1491
1566
|
],
|
|
1492
1567
|
};
|
|
1493
1568
|
}
|
|
1569
|
+
// spec12.1 §8.1, §13, §38, §64: a SnapshotRequest arriving after another
|
|
1570
|
+
// authority's commit MUST observe state at least as new as that commit. Reconcile
|
|
1571
|
+
// to the durable revision before answering — this is the protocol path a real
|
|
1572
|
+
// consumer reads authoritative state through, and it is a permanent regression
|
|
1573
|
+
// requirement.
|
|
1574
|
+
await ensureStateCoherent();
|
|
1494
1575
|
report({ kind: 'snapshot', revision: storeRevision });
|
|
1495
1576
|
const response = {
|
|
1496
1577
|
kind: 'snapshot',
|
|
@@ -1524,9 +1605,15 @@ export function createAxiomServer(options) {
|
|
|
1524
1605
|
};
|
|
1525
1606
|
});
|
|
1526
1607
|
},
|
|
1527
|
-
snapshot: snapshotOf,
|
|
1608
|
+
snapshot: () => snapshotOf(),
|
|
1528
1609
|
getState: (id) => runtime.getState(id),
|
|
1529
1610
|
revision: () => storeRevision,
|
|
1611
|
+
async coherentSnapshot() {
|
|
1612
|
+
return serialize(async () => {
|
|
1613
|
+
await ensureStateCoherent();
|
|
1614
|
+
return snapshotOf();
|
|
1615
|
+
});
|
|
1616
|
+
},
|
|
1530
1617
|
mutationLog: () => runtime.getMutationLog(),
|
|
1531
1618
|
effectLog: () => [...effectRecords.values()].map((entry) => ({ ...entry })),
|
|
1532
1619
|
subscriptionLog: () => subscriptionRuntime.status(),
|
|
@@ -1574,11 +1661,12 @@ export function createAxiomServer(options) {
|
|
|
1574
1661
|
compatibilityKey,
|
|
1575
1662
|
};
|
|
1576
1663
|
if (!distributedWorkStore) {
|
|
1577
|
-
return { authority: authorityInfo, effects: [], incompatibleEffects: [] };
|
|
1664
|
+
return { authority: authorityInfo, effects: [], schedules: [], incompatibleEffects: [] };
|
|
1578
1665
|
}
|
|
1579
1666
|
return {
|
|
1580
1667
|
authority: authorityInfo,
|
|
1581
1668
|
effects: await distributedWorkStore.list('effect'),
|
|
1669
|
+
schedules: await distributedWorkStore.list(SCHEDULE_FIRING_WORK_CLASS),
|
|
1582
1670
|
incompatibleEffects: await distributedWorkStore.listIncompatible('effect'),
|
|
1583
1671
|
};
|
|
1584
1672
|
},
|
|
@@ -9,11 +9,29 @@ import type { PersistenceAdapter } from './persistence.js';
|
|
|
9
9
|
*
|
|
10
10
|
* The whole semantic transaction is written inside one SQL transaction, so a crash cannot
|
|
11
11
|
* leave half of an action committed.
|
|
12
|
+
*
|
|
13
|
+
* **Multi-process contention (spec12.1 §27-§34).** This adapter is part of the supported
|
|
14
|
+
* SQLite multi-authority reference path: independent OS processes, independent connections,
|
|
15
|
+
* one database file. SQLite's single-writer file lock surfaces physical contention as
|
|
16
|
+
* `SQLITE_BUSY` / `SQLITE_LOCKED`; that MUST NOT escape as a semantic result during ordinary
|
|
17
|
+
* operation. Every statement runs behind a short `PRAGMA busy_timeout` plus the bounded
|
|
18
|
+
* {@link runWithBusyHandling} retry shared with the migration and coordination providers —
|
|
19
|
+
* physical contention is absorbed, a genuine constraint / corruption / IO / programmer error
|
|
20
|
+
* passes straight through, and a semantic optimistic `CONCURRENCY_CONFLICT` is never
|
|
21
|
+
* manufactured from a lock wait (§32). Waiting is always bounded (§28).
|
|
12
22
|
*/
|
|
13
23
|
export interface SqlitePersistenceOptions {
|
|
14
24
|
/** A file path, or `':memory:'`. */
|
|
15
25
|
location: string;
|
|
16
26
|
table?: string;
|
|
27
|
+
/**
|
|
28
|
+
* The SQLite native busy wait, in ms (spec12.1 §30). Infrastructure tuning, never
|
|
29
|
+
* application semantics. `0` disables the native wait; the bounded retry still applies.
|
|
30
|
+
* Defaults to the value the migration / coordination providers use.
|
|
31
|
+
*/
|
|
32
|
+
busyTimeoutMs?: number;
|
|
33
|
+
/** Test seam — deterministic sleep for the bounded busy retry. */
|
|
34
|
+
sleep?: (ms: number) => Promise<void>;
|
|
17
35
|
}
|
|
18
36
|
/** Whether this Node build offers `node:sqlite` at all. */
|
|
19
37
|
export declare function isSqliteAvailable(): Promise<boolean>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DEFAULT_BUSY_TIMEOUT_MS, SqliteContentionError, runWithBusyHandling, } from './sqlite-contention.js';
|
|
1
2
|
/** Whether this Node build offers `node:sqlite` at all. */
|
|
2
3
|
export async function isSqliteAvailable() {
|
|
3
4
|
try {
|
|
@@ -12,23 +13,33 @@ export async function createSqlitePersistence(options) {
|
|
|
12
13
|
const module = (await import('node:sqlite'));
|
|
13
14
|
const table = options.table ?? 'axiom_state';
|
|
14
15
|
const database = new module.DatabaseSync(options.location);
|
|
16
|
+
const busyMs = Math.max(0, Math.floor(options.busyTimeoutMs ?? DEFAULT_BUSY_TIMEOUT_MS));
|
|
17
|
+
const guard = (context, fn) => runWithBusyHandling(fn, {
|
|
18
|
+
context,
|
|
19
|
+
...(options.sleep !== undefined ? { sleep: options.sleep } : {}),
|
|
20
|
+
});
|
|
15
21
|
const effectsTable = `${table}_effects`;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
// Schema creation is itself a write and can hit a concurrent initializer's lock (spec12.1
|
|
23
|
+
// §33) — run it behind the same bounded handling as everything else.
|
|
24
|
+
await guard('sqlitePersistence.init', () => {
|
|
25
|
+
database.exec(`PRAGMA busy_timeout = ${busyMs};`);
|
|
26
|
+
database.exec(`
|
|
27
|
+
CREATE TABLE IF NOT EXISTS ${table} (
|
|
28
|
+
state_id TEXT PRIMARY KEY,
|
|
29
|
+
revision INTEGER NOT NULL,
|
|
30
|
+
value TEXT NOT NULL
|
|
31
|
+
);
|
|
32
|
+
CREATE TABLE IF NOT EXISTS ${table}_meta (
|
|
33
|
+
key TEXT PRIMARY KEY,
|
|
34
|
+
value INTEGER NOT NULL
|
|
35
|
+
);
|
|
36
|
+
CREATE TABLE IF NOT EXISTS ${effectsTable} (
|
|
37
|
+
effect_id TEXT PRIMARY KEY,
|
|
38
|
+
record TEXT NOT NULL,
|
|
39
|
+
status TEXT NOT NULL
|
|
40
|
+
);
|
|
41
|
+
`);
|
|
42
|
+
});
|
|
32
43
|
const readAll = database.prepare(`SELECT state_id, revision, value FROM ${table}`);
|
|
33
44
|
const readRevision = database.prepare(`SELECT value FROM ${table}_meta WHERE key = 'revision'`);
|
|
34
45
|
const readOne = database.prepare(`SELECT revision FROM ${table} WHERE state_id = ?`);
|
|
@@ -43,54 +54,79 @@ export async function createSqlitePersistence(options) {
|
|
|
43
54
|
const currentRevision = () => Number(readRevision.get()?.value ?? 0);
|
|
44
55
|
return {
|
|
45
56
|
async load() {
|
|
46
|
-
return readAll.all().map((row) => ({
|
|
57
|
+
return guard('sqlitePersistence.load', () => readAll.all().map((row) => ({
|
|
47
58
|
stateId: String(row.state_id),
|
|
48
59
|
revision: Number(row.revision),
|
|
49
60
|
value: JSON.parse(String(row.value)),
|
|
50
|
-
}));
|
|
61
|
+
})));
|
|
51
62
|
},
|
|
52
63
|
async commit(commit) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return { committed: false, revision: currentRevision(), conflicts };
|
|
58
|
-
}
|
|
59
|
-
const revision = currentRevision() + 1;
|
|
60
|
-
// One SQL transaction for one semantic transaction — the state writes and the effect
|
|
61
|
-
// intents this transaction recorded commit atomically, which is the outbox invariant
|
|
62
|
-
// (spec §18): a crash right after this call cannot lose an intent that was here.
|
|
63
|
-
database.exec('BEGIN IMMEDIATE');
|
|
64
|
+
// The whole commit — the conflict check and the atomic write — runs behind bounded
|
|
65
|
+
// busy handling. A physical lock we wait out then re-checks conflicts against the
|
|
66
|
+
// now-current revisions, so a concurrent writer's commit becomes a legitimate
|
|
67
|
+
// CONCURRENCY_CONFLICT rather than a leaked SQLITE_BUSY (spec12.1 §32).
|
|
64
68
|
try {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
return await guard('sqlitePersistence.commit', () => {
|
|
70
|
+
const conflicts = commit.writes
|
|
71
|
+
.map((write) => write.stateId)
|
|
72
|
+
.filter((stateId) => Number(readOne.get(stateId)?.revision ?? 0) !== (commit.expected[stateId] ?? 0));
|
|
73
|
+
if (conflicts.length > 0) {
|
|
74
|
+
return { committed: false, revision: currentRevision(), conflicts };
|
|
75
|
+
}
|
|
76
|
+
const revision = currentRevision() + 1;
|
|
77
|
+
database.exec('BEGIN IMMEDIATE');
|
|
78
|
+
try {
|
|
79
|
+
for (const write of commit.writes) {
|
|
80
|
+
upsert.run(write.stateId, revision, JSON.stringify(write.value ?? null));
|
|
81
|
+
}
|
|
82
|
+
setRevision.run(revision);
|
|
83
|
+
for (const effect of commit.effects ?? []) {
|
|
84
|
+
insertEffect.run(effect.id, JSON.stringify(effect), effect.status);
|
|
85
|
+
}
|
|
86
|
+
database.exec('COMMIT');
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
try {
|
|
90
|
+
database.exec('ROLLBACK');
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
/* nothing open */
|
|
94
|
+
}
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
return { committed: true, revision, conflicts: [] };
|
|
98
|
+
});
|
|
73
99
|
}
|
|
74
100
|
catch (error) {
|
|
75
|
-
|
|
101
|
+
if (error instanceof SqliteContentionError) {
|
|
102
|
+
// Physical contention outlasted the bounded window. Report a refused commit (like
|
|
103
|
+
// an optimistic conflict) so the authority reconciles and the caller may retry —
|
|
104
|
+
// never a raw provider error (spec12.1 §27, §67).
|
|
105
|
+
return {
|
|
106
|
+
committed: false,
|
|
107
|
+
revision: await guard('sqlitePersistence.revision', currentRevision),
|
|
108
|
+
conflicts: commit.writes.map((write) => write.stateId),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
76
111
|
throw error;
|
|
77
112
|
}
|
|
78
|
-
return { committed: true, revision, conflicts: [] };
|
|
79
113
|
},
|
|
80
114
|
async revision() {
|
|
81
|
-
return currentRevision
|
|
115
|
+
return guard('sqlitePersistence.revision', currentRevision);
|
|
82
116
|
},
|
|
83
117
|
async loadPendingEffects() {
|
|
84
|
-
return readPendingEffects.all().map((row) => JSON.parse(String(row.record)));
|
|
118
|
+
return guard('sqlitePersistence.loadPendingEffects', () => readPendingEffects.all().map((row) => JSON.parse(String(row.record))));
|
|
85
119
|
},
|
|
86
120
|
async recordEffectAttempt(id, update) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
121
|
+
await guard('sqlitePersistence.recordEffectAttempt', () => {
|
|
122
|
+
const row = readEffect.get(id);
|
|
123
|
+
if (!row) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const existing = JSON.parse(String(row.record));
|
|
127
|
+
const updated = { ...existing, ...update };
|
|
128
|
+
updateEffect.run(JSON.stringify(updated), updated.status, id);
|
|
129
|
+
});
|
|
94
130
|
},
|
|
95
131
|
async close() {
|
|
96
132
|
database.close();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cynodia/axiom-server",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.1-alpha.1",
|
|
4
4
|
"description": "Authoritative server runtime that executes an Axiom Server IR.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "AskTech AS",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"./schema/*": "./schema/*"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@cynodia/axiom-core": "0.12.
|
|
41
|
-
"@cynodia/axiom-runtime": "0.12.
|
|
40
|
+
"@cynodia/axiom-core": "0.12.1-alpha.1",
|
|
41
|
+
"@cynodia/axiom-runtime": "0.12.1-alpha.1"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
44
|
"build": "tsc -b tsconfig.json tsconfig.test.json",
|