@cynodia/axiom-runtime 0.3.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/LICENSE +21 -0
- package/README.md +28 -0
- package/dist/dom.d.ts +42 -0
- package/dist/dom.js +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +9 -0
- package/dist/memory-host.d.ts +52 -0
- package/dist/memory-host.js +145 -0
- package/dist/mutation/mutation-engine.d.ts +44 -0
- package/dist/mutation/mutation-engine.js +92 -0
- package/dist/mutation/resolve-location.d.ts +37 -0
- package/dist/mutation/resolve-location.js +108 -0
- package/dist/mutation/store.d.ts +14 -0
- package/dist/mutation/store.js +19 -0
- package/dist/mutation/transaction.d.ts +21 -0
- package/dist/mutation/transaction.js +45 -0
- package/dist/mutation/values.d.ts +17 -0
- package/dist/mutation/values.js +73 -0
- package/dist/runtime.d.ts +48 -0
- package/dist/runtime.js +1149 -0
- package/dist/source.d.ts +5 -0
- package/dist/source.js +71 -0
- package/package.json +40 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export function createTransactionManager(store, nextId) {
|
|
2
|
+
const open = [];
|
|
3
|
+
return {
|
|
4
|
+
currentId: () => open[0]?.id,
|
|
5
|
+
begin() {
|
|
6
|
+
const isRoot = open.length === 0;
|
|
7
|
+
const id = isRoot ? nextId() : open[0].id;
|
|
8
|
+
const entry = { id, snapshot: isRoot ? store.capture() : undefined };
|
|
9
|
+
open.push(entry);
|
|
10
|
+
let settled = false;
|
|
11
|
+
const close = () => {
|
|
12
|
+
settled = true;
|
|
13
|
+
const index = open.indexOf(entry);
|
|
14
|
+
if (index >= 0) {
|
|
15
|
+
open.splice(index, 1);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
return {
|
|
19
|
+
id,
|
|
20
|
+
isRoot,
|
|
21
|
+
commit() {
|
|
22
|
+
if (settled) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
close();
|
|
26
|
+
},
|
|
27
|
+
rollback() {
|
|
28
|
+
if (settled) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
// A nested failure unwinds to the outermost snapshot, so a whole action is
|
|
32
|
+
// rolled back atomically rather than half applied.
|
|
33
|
+
const root = open[0];
|
|
34
|
+
close();
|
|
35
|
+
if (isRoot) {
|
|
36
|
+
store.restore(entry.snapshot);
|
|
37
|
+
}
|
|
38
|
+
else if (root) {
|
|
39
|
+
store.restore(root.snapshot);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Value helpers shared by the runtime and the mutation subsystem.
|
|
3
|
+
*
|
|
4
|
+
* Stored state is deeply frozen. Any accidental write to a value read out of the store
|
|
5
|
+
* throws in strict mode, which is what keeps "no implicit object mutation" an enforced
|
|
6
|
+
* invariant rather than a convention.
|
|
7
|
+
*/
|
|
8
|
+
export declare function cloneValue<T>(value: T): T;
|
|
9
|
+
export declare function deepFreeze<T>(value: T): T;
|
|
10
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
11
|
+
/** Presence semantics used by `required` — distinct from boolean coercion. */
|
|
12
|
+
export declare function isPresent(value: unknown): boolean;
|
|
13
|
+
export declare function toBoolean(value: unknown): boolean;
|
|
14
|
+
export declare function toText(value: unknown): string;
|
|
15
|
+
export declare function compareValues(left: unknown, right: unknown): number;
|
|
16
|
+
export declare function valuesEqual(left: unknown, right: unknown): boolean;
|
|
17
|
+
//# sourceMappingURL=values.d.ts.map
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Value helpers shared by the runtime and the mutation subsystem.
|
|
3
|
+
*
|
|
4
|
+
* Stored state is deeply frozen. Any accidental write to a value read out of the store
|
|
5
|
+
* throws in strict mode, which is what keeps "no implicit object mutation" an enforced
|
|
6
|
+
* invariant rather than a convention.
|
|
7
|
+
*/
|
|
8
|
+
export function cloneValue(value) {
|
|
9
|
+
return value === undefined ? value : JSON.parse(JSON.stringify(value));
|
|
10
|
+
}
|
|
11
|
+
export function deepFreeze(value) {
|
|
12
|
+
if (value === null || typeof value !== 'object' || Object.isFrozen(value)) {
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
for (const entry of Object.values(value)) {
|
|
16
|
+
deepFreeze(entry);
|
|
17
|
+
}
|
|
18
|
+
return Object.freeze(value);
|
|
19
|
+
}
|
|
20
|
+
export function isRecord(value) {
|
|
21
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
22
|
+
}
|
|
23
|
+
/** Presence semantics used by `required` — distinct from boolean coercion. */
|
|
24
|
+
export function isPresent(value) {
|
|
25
|
+
if (value === null || value === undefined) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (typeof value === 'string') {
|
|
29
|
+
return value.trim().length > 0;
|
|
30
|
+
}
|
|
31
|
+
if (Array.isArray(value)) {
|
|
32
|
+
return value.length > 0;
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
export function toBoolean(value) {
|
|
37
|
+
if (Array.isArray(value)) {
|
|
38
|
+
return value.length > 0;
|
|
39
|
+
}
|
|
40
|
+
return Boolean(value);
|
|
41
|
+
}
|
|
42
|
+
export function toText(value) {
|
|
43
|
+
if (value === null || value === undefined) {
|
|
44
|
+
return '';
|
|
45
|
+
}
|
|
46
|
+
if (typeof value === 'string') {
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
50
|
+
return String(value);
|
|
51
|
+
}
|
|
52
|
+
return JSON.stringify(value);
|
|
53
|
+
}
|
|
54
|
+
export function compareValues(left, right) {
|
|
55
|
+
if (typeof left === 'number' && typeof right === 'number') {
|
|
56
|
+
return left === right ? 0 : left < right ? -1 : 1;
|
|
57
|
+
}
|
|
58
|
+
const leftText = toText(left);
|
|
59
|
+
const rightText = toText(right);
|
|
60
|
+
return leftText === rightText ? 0 : leftText < rightText ? -1 : 1;
|
|
61
|
+
}
|
|
62
|
+
export function valuesEqual(left, right) {
|
|
63
|
+
if (left === right) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
if (left === null || left === undefined || right === null || right === undefined) {
|
|
67
|
+
return (left ?? null) === (right ?? null);
|
|
68
|
+
}
|
|
69
|
+
if (typeof left === 'object' || typeof right === 'object') {
|
|
70
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ApplicationIR, CompiledRoute, FieldId, NodeId } from '@cynodia/axiom-core';
|
|
2
|
+
import type { DomElement, HostEnvironment } from './dom.js';
|
|
3
|
+
import type { MutationLogEntry } from './mutation/mutation-engine.js';
|
|
4
|
+
export interface RuntimeDiagnostic {
|
|
5
|
+
code: string;
|
|
6
|
+
message: string;
|
|
7
|
+
severity: 'error' | 'warning';
|
|
8
|
+
nodeId?: NodeId;
|
|
9
|
+
fieldId?: FieldId;
|
|
10
|
+
}
|
|
11
|
+
export interface ActionResult {
|
|
12
|
+
ok: boolean;
|
|
13
|
+
diagnostics: RuntimeDiagnostic[];
|
|
14
|
+
}
|
|
15
|
+
export interface RouteMatch {
|
|
16
|
+
route: CompiledRoute;
|
|
17
|
+
/** Parameter values keyed by route parameter id. */
|
|
18
|
+
parameters: Record<string, string>;
|
|
19
|
+
}
|
|
20
|
+
export type NativeImplementation = (inputs: Record<string, unknown>) => unknown;
|
|
21
|
+
/** When constraints are evaluated after an input writes to its location. */
|
|
22
|
+
export type InputValidationMode = 'immediate' | 'deferred';
|
|
23
|
+
export interface AxiomRuntimeOptions {
|
|
24
|
+
ir: ApplicationIR;
|
|
25
|
+
rootElement: DomElement;
|
|
26
|
+
host: HostEnvironment;
|
|
27
|
+
nativeOperations?: Record<string, NativeImplementation>;
|
|
28
|
+
inputValidation?: InputValidationMode;
|
|
29
|
+
/** Records previous and next values in the mutation log. */
|
|
30
|
+
recordMutationValues?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface AxiomRuntime {
|
|
33
|
+
start(): void;
|
|
34
|
+
render(): void;
|
|
35
|
+
getState(id: NodeId): unknown;
|
|
36
|
+
setState(id: NodeId, value: unknown): void;
|
|
37
|
+
invokeAction(id: NodeId, args?: Record<string, unknown>): ActionResult;
|
|
38
|
+
navigate(path: string): void;
|
|
39
|
+
currentRoute(): RouteMatch | null;
|
|
40
|
+
diagnostics(): RuntimeDiagnostic[];
|
|
41
|
+
/** Every mutation this runtime has applied, in order, with its semantic location. */
|
|
42
|
+
getMutationLog(): MutationLogEntry[];
|
|
43
|
+
registerNativeOperation(implementationId: string, implementation: NativeImplementation): void;
|
|
44
|
+
}
|
|
45
|
+
export declare function createAxiomRuntime(options: AxiomRuntimeOptions): AxiomRuntime;
|
|
46
|
+
/** Builds a host bound to the browser globals. Used by generated pages. */
|
|
47
|
+
export declare function createBrowserHost(): HostEnvironment;
|
|
48
|
+
//# sourceMappingURL=runtime.d.ts.map
|