@almadar/shell-runtime 0.0.0-stage → 0.1.0
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/chunk-2JU7Y4KU.js +21 -0
- package/dist/chunk-46RWK2B5.js +28 -0
- package/dist/chunk-4J2QEIUV.js +78 -0
- package/dist/chunk-4SEK5ER2.js +8 -0
- package/dist/chunk-4ZICQ5PM.js +192 -0
- package/dist/chunk-53IPZXDP.js +76 -0
- package/dist/chunk-5D63EMO5.js +16 -0
- package/dist/chunk-64IH2SMY.js +22 -0
- package/dist/chunk-7SDV3QWR.js +67 -0
- package/dist/chunk-CPSQXEJ6.js +55 -0
- package/dist/chunk-CQP5XAX6.js +173 -0
- package/dist/chunk-CROS2GEX.js +49 -0
- package/dist/chunk-D4XJTBLB.js +217 -0
- package/dist/chunk-D5USD2EX.js +232 -0
- package/dist/chunk-DYOJGLIX.js +25 -0
- package/dist/chunk-ELBLT2HZ.js +1 -0
- package/dist/chunk-EV56K4GB.js +63 -0
- package/dist/chunk-EZZYLTWY.js +1 -0
- package/dist/chunk-FQ6IZMIU.js +10 -0
- package/dist/chunk-FWA5K7Q2.js +48 -0
- package/dist/chunk-HC6ERSAH.js +55 -0
- package/dist/chunk-HLOUDLPI.js +48 -0
- package/dist/chunk-HMK6YMQS.js +135 -0
- package/dist/chunk-HNOAZWFH.js +19 -0
- package/dist/chunk-IHQLE5M7.js +62 -0
- package/dist/chunk-IOPUS6Q5.js +15 -0
- package/dist/chunk-JFWFDUTR.js +35 -0
- package/dist/chunk-LZIOTBFX.js +1 -0
- package/dist/chunk-MHYSHSRG.js +54 -0
- package/dist/chunk-MLKGABMK.js +7 -0
- package/dist/chunk-MZKIL5IN.js +74 -0
- package/dist/chunk-NAECVIAR.js +322 -0
- package/dist/chunk-NATGCIVM.js +583 -0
- package/dist/chunk-O57GKTYU.js +30 -0
- package/dist/chunk-ODJCZ27I.js +50 -0
- package/dist/chunk-P53CKOME.js +1 -0
- package/dist/chunk-Q2H75UYT.js +92 -0
- package/dist/chunk-Q7JK4KY7.js +9 -0
- package/dist/chunk-QELZ6TEJ.js +208 -0
- package/dist/chunk-QQRDUMB2.js +40 -0
- package/dist/chunk-RBUMDYFS.js +681 -0
- package/dist/chunk-REPX5QPI.js +57 -0
- package/dist/chunk-SAS5KKCR.js +43 -0
- package/dist/chunk-SKLMWHQD.js +109 -0
- package/dist/chunk-SOQDEPME.js +131 -0
- package/dist/chunk-SWPDU752.js +28 -0
- package/dist/chunk-T775TUCD.js +18 -0
- package/dist/chunk-UBN5JJKZ.js +26 -0
- package/dist/chunk-UJHOUZAL.js +36 -0
- package/dist/chunk-UP6GGGAN.js +34 -0
- package/dist/chunk-VMOBW7RK.js +21 -0
- package/dist/chunk-VNMG7NKY.js +69 -0
- package/dist/chunk-WNPNNYXY.js +22 -0
- package/dist/chunk-XB5GYODW.js +28 -0
- package/dist/chunk-XXWNMJI6.js +15 -0
- package/dist/chunk-YL2SO5TP.js +334 -0
- package/dist/chunk-YO4LWS3I.js +606 -0
- package/dist/chunk-ZAQSS47X.js +157 -0
- package/dist/chunk-ZCQUUJIF.js +129 -0
- package/dist/client/EventTransport.d.ts +131 -0
- package/dist/client/EventTransport.js +2 -0
- package/dist/client/ServerBridge.d.ts +126 -0
- package/dist/client/ServerBridge.js +3 -0
- package/dist/client/awaiting.d.ts +28 -0
- package/dist/client/awaiting.js +2 -0
- package/dist/client/channel-transport.d.ts +69 -0
- package/dist/client/channel-transport.js +2 -0
- package/dist/client/circuit-store.d.ts +76 -0
- package/dist/client/circuit-store.js +4 -0
- package/dist/client/client-kernel.d.ts +113 -0
- package/dist/client/client-kernel.js +1 -0
- package/dist/client/declared-input-dispatch.d.ts +5 -0
- package/dist/client/declared-input-dispatch.js +2 -0
- package/dist/client/inbound-dispatch.d.ts +5 -0
- package/dist/client/inbound-dispatch.js +2 -0
- package/dist/client/local-fold.d.ts +26 -0
- package/dist/client/local-fold.js +2 -0
- package/dist/client/render-sigils.d.ts +24 -0
- package/dist/client/render-sigils.js +2 -0
- package/dist/client/state-book.d.ts +37 -0
- package/dist/client/state-book.js +2 -0
- package/dist/client/trait-mount-error.d.ts +9 -0
- package/dist/client/trait-mount-error.js +2 -0
- package/dist/client/trait-types.d.ts +67 -0
- package/dist/client/trait-types.js +1 -0
- package/dist/credentials/env-vars.d.ts +14 -0
- package/dist/credentials/env-vars.js +3 -0
- package/dist/credentials/scope.d.ts +50 -0
- package/dist/credentials/scope.js +2 -0
- package/dist/credentials/seed.d.ts +21 -0
- package/dist/credentials/seed.js +2 -0
- package/dist/credentials/store.d.ts +110 -0
- package/dist/credentials/store.js +2 -0
- package/dist/effects/in-flight-calls.d.ts +36 -0
- package/dist/effects/in-flight-calls.js +2 -0
- package/dist/effects/on-device-model.d.ts +22 -0
- package/dist/effects/on-device-model.js +2 -0
- package/dist/events/EventBus.d.ts +90 -0
- package/dist/events/EventBus.js +2 -0
- package/dist/events/emit-stamp.d.ts +47 -0
- package/dist/events/emit-stamp.js +2 -0
- package/dist/index-hT1xCdTq.d.ts +46 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.js +59 -0
- package/dist/kernel/beacon.d.ts +32 -0
- package/dist/kernel/beacon.js +2 -0
- package/dist/kernel/decode.d.ts +15 -0
- package/dist/kernel/decode.js +2 -0
- package/dist/kernel/engine.d.ts +94 -0
- package/dist/kernel/engine.js +11 -0
- package/dist/kernel/frame-applier.d.ts +43 -0
- package/dist/kernel/frame-applier.js +2 -0
- package/dist/kernel/host.d.ts +95 -0
- package/dist/kernel/host.js +9 -0
- package/dist/kernel/http.d.ts +302 -0
- package/dist/kernel/http.js +6 -0
- package/dist/kernel/lane.d.ts +28 -0
- package/dist/kernel/lane.js +2 -0
- package/dist/kernel/oauth-effects.d.ts +25 -0
- package/dist/kernel/oauth-effects.js +7 -0
- package/dist/kernel/oauth-port.d.ts +31 -0
- package/dist/kernel/oauth-port.js +2 -0
- package/dist/kernel/os-port.d.ts +133 -0
- package/dist/kernel/os-port.js +2 -0
- package/dist/kernel/program-effects.d.ts +65 -0
- package/dist/kernel/program-effects.js +2 -0
- package/dist/kernel/wire.d.ts +66 -0
- package/dist/kernel/wire.js +1 -0
- package/dist/loader/http-loader.d.ts +111 -0
- package/dist/loader/http-loader.js +2 -0
- package/dist/loader/lazy-page.d.ts +12 -0
- package/dist/loader/lazy-page.js +2 -0
- package/dist/loader/schema-loader.d.ts +160 -0
- package/dist/loader/schema-loader.js +1 -0
- package/dist/persistence/native-memory.d.ts +28 -0
- package/dist/persistence/native-memory.js +2 -0
- package/dist/runtime/effects/composition/compose-behaviors.d.ts +58 -0
- package/dist/runtime/effects/composition/compose-behaviors.js +4 -0
- package/dist/runtime/effects/composition/event-wiring.d.ts +38 -0
- package/dist/runtime/effects/composition/event-wiring.js +2 -0
- package/dist/runtime/effects/composition/index.d.ts +5 -0
- package/dist/runtime/effects/composition/index.js +6 -0
- package/dist/runtime/effects/composition/layout-strategy.d.ts +35 -0
- package/dist/runtime/effects/composition/layout-strategy.js +2 -0
- package/dist/runtime/effects/composition/pipe.d.ts +25 -0
- package/dist/runtime/effects/composition/pipe.js +2 -0
- package/dist/runtime/entities/mockStoreSeed.d.ts +56 -0
- package/dist/runtime/entities/mockStoreSeed.js +3 -0
- package/dist/runtime/entities/seedBrowserStore.d.ts +28 -0
- package/dist/runtime/entities/seedBrowserStore.js +2 -0
- package/dist/runtime/entities/std-mock-seed.d.ts +87 -0
- package/dist/runtime/entities/std-mock-seed.js +2 -0
- package/dist/runtime/evaluation/local-store-transport.d.ts +68 -0
- package/dist/runtime/evaluation/local-store-transport.js +18 -0
- package/dist/runtime/evaluation/residence-transport.d.ts +15 -0
- package/dist/runtime/evaluation/residence-transport.js +2 -0
- package/dist/runtime/evaluation/routed-persistence.d.ts +9 -0
- package/dist/runtime/evaluation/routed-persistence.js +2 -0
- package/dist/runtime/events/identity/routing.d.ts +84 -0
- package/dist/runtime/events/identity/routing.js +2 -0
- package/dist/runtime/time/TickScheduler.d.ts +77 -0
- package/dist/runtime/time/TickScheduler.js +3 -0
- package/dist/runtime/time/cron.d.ts +25 -0
- package/dist/runtime/time/cron.js +2 -0
- package/dist/runtime/time/duration.d.ts +20 -0
- package/dist/runtime/time/duration.js +2 -0
- package/dist/runtime/traits/OrbitalTraitParsing.d.ts +34 -0
- package/dist/runtime/traits/OrbitalTraitParsing.js +2 -0
- package/dist/runtime/traits/listen-targets.d.ts +16 -0
- package/dist/runtime/traits/listen-targets.js +3 -0
- package/dist/runtime/traits/trait-index.d.ts +35 -0
- package/dist/runtime/traits/trait-index.js +3 -0
- package/dist/runtime/types.d.ts +574 -0
- package/dist/runtime/types.js +2 -0
- package/dist/service/default-call-service.d.ts +23 -0
- package/dist/service/default-call-service.js +3 -0
- package/dist/service/service-provider.d.ts +37 -0
- package/dist/service/service-provider.js +2 -0
- package/package.json +46 -3
- package/README.md +0 -3
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// src/kernel/os-port.ts
|
|
2
|
+
function kernelOsCompletions(kernel) {
|
|
3
|
+
const deferring = JSON.parse(kernel.osDeferringOps());
|
|
4
|
+
const streaming = JSON.parse(kernel.osStreamOps());
|
|
5
|
+
return {
|
|
6
|
+
defers: (op) => deferring.includes(op),
|
|
7
|
+
streams: (op) => streaming.includes(op),
|
|
8
|
+
event: (site, key, outcome) => {
|
|
9
|
+
const fired = JSON.parse(kernel.osCompletionEvent(JSON.stringify(site), key, JSON.stringify(outcome)));
|
|
10
|
+
return fired;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function kernelOsGrants(matchers, server) {
|
|
15
|
+
return {
|
|
16
|
+
allows: (need) => server().osAllows(JSON.stringify(need)),
|
|
17
|
+
globMatches: (glob, path) => matchers.osGlobMatches(glob, path),
|
|
18
|
+
isPublicAddress: (ip) => matchers.isPublicAddress(ip),
|
|
19
|
+
anchor: (anchors) => {
|
|
20
|
+
const problems = JSON.parse(server().setOsAnchors(JSON.stringify(anchors)));
|
|
21
|
+
return problems;
|
|
22
|
+
},
|
|
23
|
+
resolveArgs: (op, args) => {
|
|
24
|
+
const resolved = JSON.parse(server().osResolveArgs(op, JSON.stringify(args)));
|
|
25
|
+
return resolved;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function expandAnchor(anchors, text) {
|
|
30
|
+
const home = text === "~" ? "" : text.startsWith("~/") ? text.slice(2) : void 0;
|
|
31
|
+
const start = text === "." ? "" : text.startsWith("./") ? text.slice(2) : void 0;
|
|
32
|
+
const rest = home ?? start;
|
|
33
|
+
if (rest === void 0) return { kind: "plain" };
|
|
34
|
+
const dir = home !== void 0 ? anchors.home : anchors.start;
|
|
35
|
+
if (dir === void 0) return { kind: "unknown", message: `${text}: this host has no ${home !== void 0 ? "home" : "start"} directory` };
|
|
36
|
+
return { kind: "anchored", path: rest === "" ? dir : `${dir.replace(/\/+$/, "")}/${rest}` };
|
|
37
|
+
}
|
|
38
|
+
function anchoredForms(anchors, path) {
|
|
39
|
+
const forms = [path];
|
|
40
|
+
const under = (dir, prefix) => {
|
|
41
|
+
if (dir === void 0) return;
|
|
42
|
+
const root = dir.replace(/\/+$/, "");
|
|
43
|
+
if (path === root) forms.push(prefix);
|
|
44
|
+
else if (path.startsWith(`${root}/`)) forms.push(`${prefix}${path.slice(root.length + 1)}`);
|
|
45
|
+
};
|
|
46
|
+
under(anchors.home, "~/");
|
|
47
|
+
under(anchors.start, "./");
|
|
48
|
+
return forms;
|
|
49
|
+
}
|
|
50
|
+
function anchoredGrants(grants, anchors) {
|
|
51
|
+
return {
|
|
52
|
+
...grants,
|
|
53
|
+
allows: (need) => need.need === "fs" ? anchoredForms(anchors, need.path).some((path) => grants.allows({ ...need, path })) : grants.allows(need)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { anchoredGrants, expandAnchor, kernelOsCompletions, kernelOsGrants };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { unwrapTraitRef, normalizeCallSiteConfigToValues, isInlineTrait, isEntityCall } from '@almadar/core';
|
|
2
|
+
|
|
3
|
+
// src/runtime/traits/OrbitalTraitParsing.ts
|
|
4
|
+
function parseOrbitalTraits(orbital) {
|
|
5
|
+
const configByTrait = /* @__PURE__ */ new Map();
|
|
6
|
+
const unwrapped = (orbital.traits || []).map((t) => {
|
|
7
|
+
const { trait, config } = unwrapTraitRef(t);
|
|
8
|
+
const normalizedConfig = normalizeCallSiteConfigToValues(config);
|
|
9
|
+
if (normalizedConfig && typeof trait !== "string" && isInlineTrait(trait) && trait.name) {
|
|
10
|
+
configByTrait.set(trait.name, normalizedConfig);
|
|
11
|
+
}
|
|
12
|
+
return trait;
|
|
13
|
+
});
|
|
14
|
+
const inlineTraits = unwrapped.filter(isInlineTrait);
|
|
15
|
+
const traits = inlineTraits.map((t) => {
|
|
16
|
+
const sm = t.stateMachine;
|
|
17
|
+
return {
|
|
18
|
+
...t.id !== void 0 ? { id: t.id } : {},
|
|
19
|
+
name: t.name,
|
|
20
|
+
states: sm?.states || [],
|
|
21
|
+
transitions: sm?.transitions || [],
|
|
22
|
+
listens: t.listens
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
const entityRef = orbital.entity;
|
|
26
|
+
let entity;
|
|
27
|
+
if (typeof entityRef === "string") {
|
|
28
|
+
entity = { name: entityRef, fields: [] };
|
|
29
|
+
} else if (isEntityCall(entityRef)) {
|
|
30
|
+
const fallbackName = entityRef.name ?? entityRef.extends.replace(/\.entity$/, "");
|
|
31
|
+
entity = {
|
|
32
|
+
name: fallbackName,
|
|
33
|
+
fields: entityRef.fields ?? [],
|
|
34
|
+
...entityRef.persistence ? { persistence: entityRef.persistence } : {},
|
|
35
|
+
...entityRef.collection ? { collection: entityRef.collection } : {}
|
|
36
|
+
};
|
|
37
|
+
} else {
|
|
38
|
+
entity = entityRef;
|
|
39
|
+
}
|
|
40
|
+
return { traits, inlineTraits, configByTrait, entity };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export { parseOrbitalTraits };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// src/client/channel-transport.ts
|
|
2
|
+
var CLOSED = "The host closed the channel before answering";
|
|
3
|
+
function createChannelTransport(channel, options = {}) {
|
|
4
|
+
let next = 1;
|
|
5
|
+
const pending = /* @__PURE__ */ new Map();
|
|
6
|
+
channel.onMessage((message) => {
|
|
7
|
+
if (message.almadarChannel !== "registered" && message.almadarChannel !== "reply") return;
|
|
8
|
+
const settle = pending.get(message.id);
|
|
9
|
+
if (!settle) return;
|
|
10
|
+
pending.delete(message.id);
|
|
11
|
+
settle(message);
|
|
12
|
+
});
|
|
13
|
+
let closed = false;
|
|
14
|
+
const closedReply = (id) => ({
|
|
15
|
+
almadarChannel: "reply",
|
|
16
|
+
id,
|
|
17
|
+
response: { success: false, transitioned: false, states: {}, emittedEvents: [], error: CLOSED }
|
|
18
|
+
});
|
|
19
|
+
channel.onClose?.(() => {
|
|
20
|
+
closed = true;
|
|
21
|
+
for (const [id, settle] of pending) settle(closedReply(id));
|
|
22
|
+
pending.clear();
|
|
23
|
+
});
|
|
24
|
+
const request = (build) => new Promise((resolve) => {
|
|
25
|
+
const id = next++;
|
|
26
|
+
if (closed) {
|
|
27
|
+
resolve(closedReply(id));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
pending.set(id, resolve);
|
|
31
|
+
channel.post(build(id));
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
hostsBrowserStore: options.hostsBrowserStore === true,
|
|
35
|
+
async register(schema) {
|
|
36
|
+
const reply = await request((id) => ({ almadarChannel: "register", id, schemaName: schema.name }));
|
|
37
|
+
const held = reply.almadarChannel === "registered" && reply.held;
|
|
38
|
+
return { success: held, carriesCircuitState: false };
|
|
39
|
+
},
|
|
40
|
+
async unregister() {
|
|
41
|
+
pending.clear();
|
|
42
|
+
},
|
|
43
|
+
async send(orbitalName, req) {
|
|
44
|
+
const reply = await request((id) => ({ almadarChannel: "send", id, orbitalName, request: req }));
|
|
45
|
+
if (reply.almadarChannel !== "reply") {
|
|
46
|
+
return { success: false, transitioned: false, states: {}, emittedEvents: [], error: "The host answered out of protocol" };
|
|
47
|
+
}
|
|
48
|
+
return reply.response;
|
|
49
|
+
},
|
|
50
|
+
subscribe(onPush) {
|
|
51
|
+
return channel.onMessage((message) => {
|
|
52
|
+
if (message.almadarChannel === "push") onPush(message.emitted, message.target);
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
subscribeHostDispatches(onDispatch) {
|
|
56
|
+
return channel.onMessage((message) => {
|
|
57
|
+
if (message.almadarChannel === "dispatched") onDispatch(message.orbitalName, message.request, message.response);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function serveChannel(channel, transport, options) {
|
|
63
|
+
const stop = channel.onMessage((message) => {
|
|
64
|
+
if (message.almadarChannel === "register") {
|
|
65
|
+
channel.post({ almadarChannel: "registered", id: message.id, held: message.schemaName === options.schemaName });
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (message.almadarChannel !== "send") return;
|
|
69
|
+
transport.send(message.orbitalName, message.request).then(
|
|
70
|
+
(response) => channel.post({ almadarChannel: "reply", id: message.id, response }),
|
|
71
|
+
(err) => channel.post({
|
|
72
|
+
almadarChannel: "reply",
|
|
73
|
+
id: message.id,
|
|
74
|
+
response: { success: false, transitioned: false, states: {}, emittedEvents: [], error: err.message }
|
|
75
|
+
})
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
push: (emitted, target) => channel.post({ almadarChannel: "push", emitted, target }),
|
|
80
|
+
pushDispatch: (orbitalName, request, response) => channel.post({ almadarChannel: "dispatched", orbitalName, request, response }),
|
|
81
|
+
stop
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function holdUntilServed(channel) {
|
|
85
|
+
let held = [];
|
|
86
|
+
const listeners = [];
|
|
87
|
+
channel.onMessage((message) => {
|
|
88
|
+
if (held !== void 0) held.push(message);
|
|
89
|
+
else listeners.forEach((l) => l(message));
|
|
90
|
+
});
|
|
91
|
+
return {
|
|
92
|
+
post: (message) => channel.post(message),
|
|
93
|
+
onMessage(listener) {
|
|
94
|
+
listeners.push(listener);
|
|
95
|
+
if (held !== void 0) {
|
|
96
|
+
const early = held;
|
|
97
|
+
held = void 0;
|
|
98
|
+
early.forEach((m) => listeners.forEach((l) => l(m)));
|
|
99
|
+
}
|
|
100
|
+
return () => {
|
|
101
|
+
const i = listeners.indexOf(listener);
|
|
102
|
+
if (i >= 0) listeners.splice(i, 1);
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
...channel.onClose !== void 0 ? { onClose: channel.onClose.bind(channel) } : {}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export { createChannelTransport, holdUntilServed, serveChannel };
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { createLogger } from '@almadar/logger';
|
|
2
|
+
|
|
3
|
+
// src/events/EventBus.ts
|
|
4
|
+
var log = createLogger("almadar:runtime:eventbus");
|
|
5
|
+
var EventBus = class {
|
|
6
|
+
listeners = /* @__PURE__ */ new Map();
|
|
7
|
+
/** Maximum recursion depth before circuit breaker activates (RCG-05) */
|
|
8
|
+
maxDepth;
|
|
9
|
+
/** Current emission depth for circular loop detection */
|
|
10
|
+
depth = 0;
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
void options.debug;
|
|
13
|
+
this.maxDepth = options.maxDepth ?? 10;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Emit an event to all registered listeners.
|
|
17
|
+
*
|
|
18
|
+
* Includes circuit breaker (RCG-05): if emit is called recursively
|
|
19
|
+
* beyond `maxDepth`, the event is dropped and an error is logged.
|
|
20
|
+
* This prevents infinite loops from circular emit/listen chains.
|
|
21
|
+
*/
|
|
22
|
+
emit(type, payload, source, routingKey) {
|
|
23
|
+
if (this.depth >= this.maxDepth) {
|
|
24
|
+
log.error("circular event loop dropped", { type, depth: this.depth, maxDepth: this.maxDepth });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const event = {
|
|
28
|
+
type,
|
|
29
|
+
payload,
|
|
30
|
+
timestamp: Date.now(),
|
|
31
|
+
source
|
|
32
|
+
};
|
|
33
|
+
const deliveryKey = routingKey ?? type;
|
|
34
|
+
const primary = this.listeners.get(deliveryKey);
|
|
35
|
+
const secondary = routingKey !== void 0 && routingKey !== type ? this.listeners.get(type) : void 0;
|
|
36
|
+
const listenerCount = (primary?.size ?? 0) + (secondary?.size ?? 0);
|
|
37
|
+
if (listenerCount > 0) {
|
|
38
|
+
log.debug("emit", { type, listenerCount, depth: this.depth });
|
|
39
|
+
} else {
|
|
40
|
+
log.debug("emit no listeners", { type });
|
|
41
|
+
}
|
|
42
|
+
this.depth++;
|
|
43
|
+
try {
|
|
44
|
+
const delivered = /* @__PURE__ */ new Set();
|
|
45
|
+
const deliverTo = (set) => {
|
|
46
|
+
if (!set) return;
|
|
47
|
+
for (const listener of Array.from(set)) {
|
|
48
|
+
if (delivered.has(listener)) continue;
|
|
49
|
+
delivered.add(listener);
|
|
50
|
+
try {
|
|
51
|
+
listener(event);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
log.error("listener threw", { type, error: error instanceof Error ? error : String(error) });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
deliverTo(primary);
|
|
58
|
+
deliverTo(secondary);
|
|
59
|
+
if (type !== "*") {
|
|
60
|
+
const wildcardListeners = this.listeners.get("*");
|
|
61
|
+
if (wildcardListeners) {
|
|
62
|
+
for (const listener of Array.from(wildcardListeners)) {
|
|
63
|
+
try {
|
|
64
|
+
listener(event);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
log.error("wildcard listener threw", { error: error instanceof Error ? error : String(error) });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
} finally {
|
|
72
|
+
this.depth--;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Subscribe to an event type
|
|
77
|
+
*/
|
|
78
|
+
on(type, listener) {
|
|
79
|
+
if (!this.listeners.has(type)) {
|
|
80
|
+
this.listeners.set(type, /* @__PURE__ */ new Set());
|
|
81
|
+
}
|
|
82
|
+
const listeners = this.listeners.get(type);
|
|
83
|
+
listeners.add(listener);
|
|
84
|
+
log.debug("subscribe", { type, total: listeners.size });
|
|
85
|
+
return () => {
|
|
86
|
+
listeners.delete(listener);
|
|
87
|
+
log.debug("unsubscribe", { type, remaining: listeners.size });
|
|
88
|
+
if (listeners.size === 0) {
|
|
89
|
+
this.listeners.delete(type);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Subscribe to ALL events (wildcard listener)
|
|
95
|
+
* Useful for event tracking, logging, debugging
|
|
96
|
+
*/
|
|
97
|
+
onAny(listener) {
|
|
98
|
+
return this.on("*", listener);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Check if there are listeners for an event type
|
|
102
|
+
*/
|
|
103
|
+
hasListeners(type) {
|
|
104
|
+
const listeners = this.listeners.get(type);
|
|
105
|
+
return listeners !== void 0 && listeners.size > 0;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Get all registered event types
|
|
109
|
+
*/
|
|
110
|
+
getRegisteredEvents() {
|
|
111
|
+
return Array.from(this.listeners.keys());
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Clear all listeners
|
|
115
|
+
*/
|
|
116
|
+
clear() {
|
|
117
|
+
log.debug("clear", { eventTypeCount: this.listeners.size });
|
|
118
|
+
this.listeners.clear();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Get listener count for an event type (for testing)
|
|
122
|
+
*/
|
|
123
|
+
getListenerCount(type) {
|
|
124
|
+
return this.listeners.get(type)?.size ?? 0;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
function createEventBus(options) {
|
|
128
|
+
return new EventBus(options);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export { EventBus, createEventBus };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { isJsonObject } from '@almadar/core';
|
|
2
|
+
|
|
3
|
+
// src/credentials/seed.ts
|
|
4
|
+
function planRows(json) {
|
|
5
|
+
const rows = JSON.parse(json);
|
|
6
|
+
if (!Array.isArray(rows)) throw new Error("the kernel seed plan is not a list");
|
|
7
|
+
return rows.map((row) => {
|
|
8
|
+
if (!isJsonObject(row) || typeof row.service !== "string" || typeof row.envVar !== "string") {
|
|
9
|
+
throw new Error("the kernel seed plan holds a row that is not { service, envVar }");
|
|
10
|
+
}
|
|
11
|
+
return { service: row.service, envVar: row.envVar };
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
async function seedCredentialStore(store, declared, env, kernel) {
|
|
15
|
+
if (!store.enabled) return 0;
|
|
16
|
+
const names = [...new Set(declared.map((d) => d.envVar))];
|
|
17
|
+
const held = names.filter((name) => store.resolve(name) !== void 0);
|
|
18
|
+
const present = names.filter((name) => (env[name] ?? "") !== "");
|
|
19
|
+
const plan = planRows(kernel.credentialSeedPlan(JSON.stringify(declared), JSON.stringify(held), JSON.stringify(present)));
|
|
20
|
+
for (const { service, envVar } of plan) {
|
|
21
|
+
const value = env[envVar];
|
|
22
|
+
if (value === void 0) throw new Error(`the kernel seeded ${envVar}, which the env does not hold`);
|
|
23
|
+
await store.set(service, envVar, value);
|
|
24
|
+
}
|
|
25
|
+
return plan.length;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { seedCredentialStore };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// src/runtime/entities/seedBrowserStore.ts
|
|
2
|
+
async function isEmpty(store, entityType) {
|
|
3
|
+
return store.countRows ? await store.countRows(entityType) === 0 : (await store.list(entityType)).length === 0;
|
|
4
|
+
}
|
|
5
|
+
async function seedBrowserStore(store, entities, locale, host) {
|
|
6
|
+
const empty = [];
|
|
7
|
+
for (const entity of entities) {
|
|
8
|
+
if (entity.browser === true && await isEmpty(store, entity.name)) empty.push(entity);
|
|
9
|
+
}
|
|
10
|
+
if (empty.length === 0) return;
|
|
11
|
+
const program = {
|
|
12
|
+
name: "BrowserStoreSeed",
|
|
13
|
+
orbitals: empty.map((entity, index) => ({ name: `BrowserStore${index}`, entity, traits: [], pages: [] }))
|
|
14
|
+
};
|
|
15
|
+
await host.seeder.dataset(program, { nowMs: host.nowMs, ...locale !== void 0 ? { locale } : {} });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { seedBrowserStore };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// src/service/service-provider.ts
|
|
2
|
+
var ServiceCallError = class extends Error {
|
|
3
|
+
constructor(message, code) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.code = code;
|
|
6
|
+
this.name = "ServiceCallError";
|
|
7
|
+
}
|
|
8
|
+
code;
|
|
9
|
+
};
|
|
10
|
+
var providers = /* @__PURE__ */ new Map();
|
|
11
|
+
function registerServiceProvider(service, provider) {
|
|
12
|
+
providers.set(service, provider);
|
|
13
|
+
}
|
|
14
|
+
function unregisterServiceProvider(service) {
|
|
15
|
+
providers.delete(service);
|
|
16
|
+
}
|
|
17
|
+
function registeredServices() {
|
|
18
|
+
return [...providers.keys()];
|
|
19
|
+
}
|
|
20
|
+
async function callRegisteredService(service, action, params, context) {
|
|
21
|
+
const provider = providers.get(service);
|
|
22
|
+
if (provider === void 0) throw new Error(`Unknown integration: ${service}`);
|
|
23
|
+
return provider(action, params ?? {}, context);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { ServiceCallError, callRegisteredService, registerServiceProvider, registeredServices, unregisterServiceProvider };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { storesRowsInBrowser } from '@almadar/core';
|
|
2
|
+
|
|
3
|
+
// src/runtime/evaluation/residence-transport.ts
|
|
4
|
+
function createResidenceTransport(options) {
|
|
5
|
+
const { local, remote, traitIndex } = options;
|
|
6
|
+
const browserStored = new Set(traitIndex.allEntities.filter(storesRowsInBrowser).map((e) => e.name));
|
|
7
|
+
const route = (orbitalName, request) => {
|
|
8
|
+
if (request.residence === "browser") return local;
|
|
9
|
+
if (request.residence === "server") {
|
|
10
|
+
if (remote === void 0) throw new Error(`residence: a ${orbitalName} ${request.event} leg needs server data, and this host has no server`);
|
|
11
|
+
return remote;
|
|
12
|
+
}
|
|
13
|
+
return remote ?? local;
|
|
14
|
+
};
|
|
15
|
+
return {
|
|
16
|
+
async register(schema) {
|
|
17
|
+
const result = await local.register(schema);
|
|
18
|
+
return remote ? remote.register(schema) : result;
|
|
19
|
+
},
|
|
20
|
+
async unregister() {
|
|
21
|
+
await local.unregister();
|
|
22
|
+
await remote?.unregister();
|
|
23
|
+
},
|
|
24
|
+
async send(orbitalName, request) {
|
|
25
|
+
return route(orbitalName, request).send(orbitalName, request);
|
|
26
|
+
},
|
|
27
|
+
async resolveRows(reference) {
|
|
28
|
+
const owner = browserStored.has(reference.entityType) ? local : remote;
|
|
29
|
+
if (owner?.resolveRows === void 0) throw new Error(`residence: no transport resolves ${reference.entityType} row sets`);
|
|
30
|
+
return owner.resolveRows(reference);
|
|
31
|
+
},
|
|
32
|
+
...remote?.subscribe ? { subscribe: remote.subscribe.bind(remote) } : {}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { createResidenceTransport };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { isPageReference, isRoutePage, themeDataKey } from '@almadar/core';
|
|
2
|
+
|
|
3
|
+
// src/client/render-sigils.ts
|
|
4
|
+
var DEFAULT_THEME_KEY = "minimalist-light";
|
|
5
|
+
function inlineNavItems(pages) {
|
|
6
|
+
const items = [];
|
|
7
|
+
for (const page of pages) {
|
|
8
|
+
if (isPageReference(page)) continue;
|
|
9
|
+
const p = page;
|
|
10
|
+
if (!isRoutePage(p) || typeof p.label !== "string" || p.path.includes(":")) continue;
|
|
11
|
+
const item = { href: p.path, label: p.label };
|
|
12
|
+
if (typeof p.icon === "string") item.icon = p.icon;
|
|
13
|
+
if (p.roles !== void 0) item.roles = [...p.roles];
|
|
14
|
+
items.push(item);
|
|
15
|
+
}
|
|
16
|
+
return items;
|
|
17
|
+
}
|
|
18
|
+
function appNavItems(orbitals) {
|
|
19
|
+
const items = [];
|
|
20
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21
|
+
for (const orbital of orbitals) {
|
|
22
|
+
for (const item of inlineNavItems(orbital.pages ?? [])) {
|
|
23
|
+
if (seen.has(item.href)) continue;
|
|
24
|
+
seen.add(item.href);
|
|
25
|
+
items.push(item);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return items;
|
|
29
|
+
}
|
|
30
|
+
function sigilThemeKey(orbitalTheme, appThemeKey) {
|
|
31
|
+
return themeDataKey(orbitalTheme) || appThemeKey || DEFAULT_THEME_KEY;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { DEFAULT_THEME_KEY, appNavItems, inlineNavItems, sigilThemeKey };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// src/kernel/beacon.ts
|
|
2
|
+
async function deliverBeacon(url, text, ports) {
|
|
3
|
+
try {
|
|
4
|
+
if (ports.queue !== void 0 && ports.queue(url, text)) return { via: "queue" };
|
|
5
|
+
} catch {
|
|
6
|
+
}
|
|
7
|
+
try {
|
|
8
|
+
const { status } = await ports.fetch(url, {
|
|
9
|
+
method: "POST",
|
|
10
|
+
body: text,
|
|
11
|
+
keepalive: true,
|
|
12
|
+
mode: "cors",
|
|
13
|
+
headers: { "Content-Type": "text/plain" }
|
|
14
|
+
});
|
|
15
|
+
return { via: "fetch", status };
|
|
16
|
+
} catch {
|
|
17
|
+
return { via: "fetch", status: null };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { deliverBeacon };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { defaultCallService } from './chunk-4SEK5ER2.js';
|
|
2
|
+
import { routeByEntity } from './chunk-O57GKTYU.js';
|
|
3
|
+
import { buildTraitIndex } from './chunk-CPSQXEJ6.js';
|
|
4
|
+
import { KernelEngine, evaluateOnKernel } from './chunk-D4XJTBLB.js';
|
|
5
|
+
import { InFlightCalls } from './chunk-MZKIL5IN.js';
|
|
6
|
+
import { createInProcessTransport } from './chunk-QELZ6TEJ.js';
|
|
7
|
+
import { orbitalInlineEntities, storesRowsInBrowser, inlineTraitRefs, applyRowAccess, readableEntitiesOf, externalInputsOf, entityOwnerOrbital } from '@almadar/core';
|
|
8
|
+
import { entityAccessPolicies } from '@almadar/core/mock';
|
|
9
|
+
|
|
10
|
+
function localStorePersistence(schema, store, memory) {
|
|
11
|
+
const browserStored = new Set(schema.orbitals.flatMap(orbitalInlineEntities).filter(storesRowsInBrowser).map((entity) => entity.name));
|
|
12
|
+
return routeByEntity(browserStored, store, memory);
|
|
13
|
+
}
|
|
14
|
+
function openLocalKernel(options) {
|
|
15
|
+
const { schema, persistence } = options;
|
|
16
|
+
options.kernel.installLanguage(options.language);
|
|
17
|
+
const callService = options.callService ?? defaultCallService;
|
|
18
|
+
const engine = KernelEngine.open(options.kernel, { ...schema, orbitals: inlineTraitRefs(schema.orbitals) }, {
|
|
19
|
+
persistence,
|
|
20
|
+
callService: (service, action, params, context) => callService(service, action, params, context),
|
|
21
|
+
programEffect: () => void 0,
|
|
22
|
+
inFlightCalls: new InFlightCalls(),
|
|
23
|
+
servicePorts: options.servicePorts ?? ((caller, user, message) => ({
|
|
24
|
+
caller,
|
|
25
|
+
message,
|
|
26
|
+
inputs: () => externalInputsOf(schema),
|
|
27
|
+
readableEntities: () => readableEntitiesOf(schema),
|
|
28
|
+
dispatchInput: () => Promise.reject(new Error("a local store host takes no input dispatch")),
|
|
29
|
+
read: async (entity) => applyRowAccess(await persistence.list(entity), entityAccessPolicies(schema, entity)?.read, void 0, { user }, entity)
|
|
30
|
+
})),
|
|
31
|
+
deliverLive: () => void 0,
|
|
32
|
+
deferCallOutcome: options.deferCallOutcome ?? (() => void 0),
|
|
33
|
+
...options.pageFacts !== void 0 ? { pageFacts: options.pageFacts } : {},
|
|
34
|
+
...options.http !== void 0 ? { http: options.http } : {},
|
|
35
|
+
...options.onDevice !== void 0 ? { onDevice: options.onDevice } : {},
|
|
36
|
+
...options.tabs !== void 0 ? { tabs: options.tabs } : {}
|
|
37
|
+
});
|
|
38
|
+
if (options.messages !== void 0) engine.setMessages(Object.keys(options.messages), options.messages);
|
|
39
|
+
engine.setTickMount([...buildTraitIndex(schema.orbitals).byName.keys()]);
|
|
40
|
+
return engine;
|
|
41
|
+
}
|
|
42
|
+
function createLocalStoreTransport(options) {
|
|
43
|
+
return createInProcessTransport(
|
|
44
|
+
async (orbital, request) => evaluateOnKernel(
|
|
45
|
+
{
|
|
46
|
+
engine: await options.kernelEngine,
|
|
47
|
+
orbital,
|
|
48
|
+
...options.outsideEventQueue !== void 0 ? { outsideEventQueue: options.outsideEventQueue } : {}
|
|
49
|
+
},
|
|
50
|
+
request,
|
|
51
|
+
await options.user,
|
|
52
|
+
request.locale
|
|
53
|
+
),
|
|
54
|
+
{
|
|
55
|
+
carriesCircuitState: false,
|
|
56
|
+
...options.schema !== void 0 ? { resolveRows: resolveOn(options, options.schema) } : {}
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
function resolveOn(options, schema) {
|
|
61
|
+
return async (reference) => resolveLocalRows(await options.kernelEngine, schema, await options.user, reference);
|
|
62
|
+
}
|
|
63
|
+
function resolveLocalRows(engine, schema, user, reference) {
|
|
64
|
+
const owner = entityOwnerOrbital(schema, reference.entityType);
|
|
65
|
+
if (owner === void 0) return Promise.reject(new Error(`${reference.entityType}: no orbital of the local program declares this entity`));
|
|
66
|
+
return engine.resolveRows(reference, { orbital: owner.name, ...user !== void 0 ? { user } : {} });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export { createLocalStoreTransport, localStorePersistence, openLocalKernel, resolveLocalRows };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { getStdEffectOperators, getOperatorRunsOn } from '@almadar/std/registry';
|
|
2
|
+
|
|
3
|
+
// src/runtime/types.ts
|
|
4
|
+
function isEffectTuple(value) {
|
|
5
|
+
return Array.isArray(value) && typeof value[0] === "string";
|
|
6
|
+
}
|
|
7
|
+
var EFFECT_ALIASES = { render: "render-ui" };
|
|
8
|
+
function withAliases(ops) {
|
|
9
|
+
const aliases = Object.keys(EFFECT_ALIASES).filter((alias) => ops.includes(EFFECT_ALIASES[alias]));
|
|
10
|
+
return [...ops, ...aliases].sort();
|
|
11
|
+
}
|
|
12
|
+
function effectsNotOn(site) {
|
|
13
|
+
return withAliases(getStdEffectOperators().filter((op) => getOperatorRunsOn(op) !== site));
|
|
14
|
+
}
|
|
15
|
+
var HANDLER_MANIFEST = {
|
|
16
|
+
client: effectsNotOn("server"),
|
|
17
|
+
server: effectsNotOn("client"),
|
|
18
|
+
test: withAliases(getStdEffectOperators()),
|
|
19
|
+
ssr: withAliases(["render-ui", "fetch", "emit", "set", "log", "ref", "deref"])
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export { HANDLER_MANIFEST, isEffectTuple };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { parseListenSource } from './chunk-IHQLE5M7.js';
|
|
2
|
+
import { applyListenMapping, kernelEvaluator, toWireValue } from '@almadar/core';
|
|
3
|
+
|
|
4
|
+
function listenGuardHolds(guard, payload) {
|
|
5
|
+
const outcome = kernelEvaluator().evaluateGuard({ payload: toWireValue(payload ?? {}) }, guard);
|
|
6
|
+
return outcome.kind === "value" && outcome.value;
|
|
7
|
+
}
|
|
8
|
+
function collectListenerTargets(traitIndex, sourceStamp, event, payload) {
|
|
9
|
+
const targets = [];
|
|
10
|
+
for (const [listenerName, listenerEntry] of traitIndex.byName) {
|
|
11
|
+
for (const listener of listenerEntry.traitDef.listens ?? []) {
|
|
12
|
+
const { bareEvent, matcher } = parseListenSource(listener, listenerEntry.orbitalName);
|
|
13
|
+
if (bareEvent !== event || !matcher(sourceStamp)) continue;
|
|
14
|
+
if (listener.guard !== void 0 && !listenGuardHolds(listener.guard, payload)) continue;
|
|
15
|
+
targets.push({
|
|
16
|
+
listenerTrait: listenerName,
|
|
17
|
+
entry: listenerEntry,
|
|
18
|
+
listener,
|
|
19
|
+
triggers: listener.triggers,
|
|
20
|
+
...listener.triggersId !== void 0 ? { triggersId: listener.triggersId } : {},
|
|
21
|
+
payload: applyListenMapping(listener.payloadMapping, payload)
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return targets;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { collectListenerTargets };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/loader/lazy-page.ts
|
|
2
|
+
async function loadLazyPage(loader, page, importerPath) {
|
|
3
|
+
const loaded = await loader.load(page.orbRef, importerPath);
|
|
4
|
+
if (!loaded.success) return loaded;
|
|
5
|
+
const { schema, sourcePath } = loaded.data;
|
|
6
|
+
if (!schema.orbitals.some((o) => o.name === page.orbital)) {
|
|
7
|
+
return {
|
|
8
|
+
success: false,
|
|
9
|
+
error: `Lazy page ${page.path} expects orbital "${page.orbital}" in ${sourcePath}, found: ${schema.orbitals.map((o) => o.name).join(", ")}`
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
return { success: true, data: schema };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { loadLazyPage };
|