@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.
Files changed (180) hide show
  1. package/dist/chunk-2JU7Y4KU.js +21 -0
  2. package/dist/chunk-46RWK2B5.js +28 -0
  3. package/dist/chunk-4J2QEIUV.js +78 -0
  4. package/dist/chunk-4SEK5ER2.js +8 -0
  5. package/dist/chunk-4ZICQ5PM.js +192 -0
  6. package/dist/chunk-53IPZXDP.js +76 -0
  7. package/dist/chunk-5D63EMO5.js +16 -0
  8. package/dist/chunk-64IH2SMY.js +22 -0
  9. package/dist/chunk-7SDV3QWR.js +67 -0
  10. package/dist/chunk-CPSQXEJ6.js +55 -0
  11. package/dist/chunk-CQP5XAX6.js +173 -0
  12. package/dist/chunk-CROS2GEX.js +49 -0
  13. package/dist/chunk-D4XJTBLB.js +217 -0
  14. package/dist/chunk-D5USD2EX.js +232 -0
  15. package/dist/chunk-DYOJGLIX.js +25 -0
  16. package/dist/chunk-ELBLT2HZ.js +1 -0
  17. package/dist/chunk-EV56K4GB.js +63 -0
  18. package/dist/chunk-EZZYLTWY.js +1 -0
  19. package/dist/chunk-FQ6IZMIU.js +10 -0
  20. package/dist/chunk-FWA5K7Q2.js +48 -0
  21. package/dist/chunk-HC6ERSAH.js +55 -0
  22. package/dist/chunk-HLOUDLPI.js +48 -0
  23. package/dist/chunk-HMK6YMQS.js +135 -0
  24. package/dist/chunk-HNOAZWFH.js +19 -0
  25. package/dist/chunk-IHQLE5M7.js +62 -0
  26. package/dist/chunk-IOPUS6Q5.js +15 -0
  27. package/dist/chunk-JFWFDUTR.js +35 -0
  28. package/dist/chunk-LZIOTBFX.js +1 -0
  29. package/dist/chunk-MHYSHSRG.js +54 -0
  30. package/dist/chunk-MLKGABMK.js +7 -0
  31. package/dist/chunk-MZKIL5IN.js +74 -0
  32. package/dist/chunk-NAECVIAR.js +322 -0
  33. package/dist/chunk-NATGCIVM.js +583 -0
  34. package/dist/chunk-O57GKTYU.js +30 -0
  35. package/dist/chunk-ODJCZ27I.js +50 -0
  36. package/dist/chunk-P53CKOME.js +1 -0
  37. package/dist/chunk-Q2H75UYT.js +92 -0
  38. package/dist/chunk-Q7JK4KY7.js +9 -0
  39. package/dist/chunk-QELZ6TEJ.js +208 -0
  40. package/dist/chunk-QQRDUMB2.js +40 -0
  41. package/dist/chunk-RBUMDYFS.js +681 -0
  42. package/dist/chunk-REPX5QPI.js +57 -0
  43. package/dist/chunk-SAS5KKCR.js +43 -0
  44. package/dist/chunk-SKLMWHQD.js +109 -0
  45. package/dist/chunk-SOQDEPME.js +131 -0
  46. package/dist/chunk-SWPDU752.js +28 -0
  47. package/dist/chunk-T775TUCD.js +18 -0
  48. package/dist/chunk-UBN5JJKZ.js +26 -0
  49. package/dist/chunk-UJHOUZAL.js +36 -0
  50. package/dist/chunk-UP6GGGAN.js +34 -0
  51. package/dist/chunk-VMOBW7RK.js +21 -0
  52. package/dist/chunk-VNMG7NKY.js +69 -0
  53. package/dist/chunk-WNPNNYXY.js +22 -0
  54. package/dist/chunk-XB5GYODW.js +28 -0
  55. package/dist/chunk-XXWNMJI6.js +15 -0
  56. package/dist/chunk-YL2SO5TP.js +334 -0
  57. package/dist/chunk-YO4LWS3I.js +606 -0
  58. package/dist/chunk-ZAQSS47X.js +157 -0
  59. package/dist/chunk-ZCQUUJIF.js +129 -0
  60. package/dist/client/EventTransport.d.ts +131 -0
  61. package/dist/client/EventTransport.js +2 -0
  62. package/dist/client/ServerBridge.d.ts +126 -0
  63. package/dist/client/ServerBridge.js +3 -0
  64. package/dist/client/awaiting.d.ts +28 -0
  65. package/dist/client/awaiting.js +2 -0
  66. package/dist/client/channel-transport.d.ts +69 -0
  67. package/dist/client/channel-transport.js +2 -0
  68. package/dist/client/circuit-store.d.ts +76 -0
  69. package/dist/client/circuit-store.js +4 -0
  70. package/dist/client/client-kernel.d.ts +113 -0
  71. package/dist/client/client-kernel.js +1 -0
  72. package/dist/client/declared-input-dispatch.d.ts +5 -0
  73. package/dist/client/declared-input-dispatch.js +2 -0
  74. package/dist/client/inbound-dispatch.d.ts +5 -0
  75. package/dist/client/inbound-dispatch.js +2 -0
  76. package/dist/client/local-fold.d.ts +26 -0
  77. package/dist/client/local-fold.js +2 -0
  78. package/dist/client/render-sigils.d.ts +24 -0
  79. package/dist/client/render-sigils.js +2 -0
  80. package/dist/client/state-book.d.ts +37 -0
  81. package/dist/client/state-book.js +2 -0
  82. package/dist/client/trait-mount-error.d.ts +9 -0
  83. package/dist/client/trait-mount-error.js +2 -0
  84. package/dist/client/trait-types.d.ts +67 -0
  85. package/dist/client/trait-types.js +1 -0
  86. package/dist/credentials/env-vars.d.ts +14 -0
  87. package/dist/credentials/env-vars.js +3 -0
  88. package/dist/credentials/scope.d.ts +50 -0
  89. package/dist/credentials/scope.js +2 -0
  90. package/dist/credentials/seed.d.ts +21 -0
  91. package/dist/credentials/seed.js +2 -0
  92. package/dist/credentials/store.d.ts +110 -0
  93. package/dist/credentials/store.js +2 -0
  94. package/dist/effects/in-flight-calls.d.ts +36 -0
  95. package/dist/effects/in-flight-calls.js +2 -0
  96. package/dist/effects/on-device-model.d.ts +22 -0
  97. package/dist/effects/on-device-model.js +2 -0
  98. package/dist/events/EventBus.d.ts +90 -0
  99. package/dist/events/EventBus.js +2 -0
  100. package/dist/events/emit-stamp.d.ts +47 -0
  101. package/dist/events/emit-stamp.js +2 -0
  102. package/dist/index-hT1xCdTq.d.ts +46 -0
  103. package/dist/index.d.ts +60 -0
  104. package/dist/index.js +59 -0
  105. package/dist/kernel/beacon.d.ts +32 -0
  106. package/dist/kernel/beacon.js +2 -0
  107. package/dist/kernel/decode.d.ts +15 -0
  108. package/dist/kernel/decode.js +2 -0
  109. package/dist/kernel/engine.d.ts +94 -0
  110. package/dist/kernel/engine.js +11 -0
  111. package/dist/kernel/frame-applier.d.ts +43 -0
  112. package/dist/kernel/frame-applier.js +2 -0
  113. package/dist/kernel/host.d.ts +95 -0
  114. package/dist/kernel/host.js +9 -0
  115. package/dist/kernel/http.d.ts +302 -0
  116. package/dist/kernel/http.js +6 -0
  117. package/dist/kernel/lane.d.ts +28 -0
  118. package/dist/kernel/lane.js +2 -0
  119. package/dist/kernel/oauth-effects.d.ts +25 -0
  120. package/dist/kernel/oauth-effects.js +7 -0
  121. package/dist/kernel/oauth-port.d.ts +31 -0
  122. package/dist/kernel/oauth-port.js +2 -0
  123. package/dist/kernel/os-port.d.ts +133 -0
  124. package/dist/kernel/os-port.js +2 -0
  125. package/dist/kernel/program-effects.d.ts +65 -0
  126. package/dist/kernel/program-effects.js +2 -0
  127. package/dist/kernel/wire.d.ts +66 -0
  128. package/dist/kernel/wire.js +1 -0
  129. package/dist/loader/http-loader.d.ts +111 -0
  130. package/dist/loader/http-loader.js +2 -0
  131. package/dist/loader/lazy-page.d.ts +12 -0
  132. package/dist/loader/lazy-page.js +2 -0
  133. package/dist/loader/schema-loader.d.ts +160 -0
  134. package/dist/loader/schema-loader.js +1 -0
  135. package/dist/persistence/native-memory.d.ts +28 -0
  136. package/dist/persistence/native-memory.js +2 -0
  137. package/dist/runtime/effects/composition/compose-behaviors.d.ts +58 -0
  138. package/dist/runtime/effects/composition/compose-behaviors.js +4 -0
  139. package/dist/runtime/effects/composition/event-wiring.d.ts +38 -0
  140. package/dist/runtime/effects/composition/event-wiring.js +2 -0
  141. package/dist/runtime/effects/composition/index.d.ts +5 -0
  142. package/dist/runtime/effects/composition/index.js +6 -0
  143. package/dist/runtime/effects/composition/layout-strategy.d.ts +35 -0
  144. package/dist/runtime/effects/composition/layout-strategy.js +2 -0
  145. package/dist/runtime/effects/composition/pipe.d.ts +25 -0
  146. package/dist/runtime/effects/composition/pipe.js +2 -0
  147. package/dist/runtime/entities/mockStoreSeed.d.ts +56 -0
  148. package/dist/runtime/entities/mockStoreSeed.js +3 -0
  149. package/dist/runtime/entities/seedBrowserStore.d.ts +28 -0
  150. package/dist/runtime/entities/seedBrowserStore.js +2 -0
  151. package/dist/runtime/entities/std-mock-seed.d.ts +87 -0
  152. package/dist/runtime/entities/std-mock-seed.js +2 -0
  153. package/dist/runtime/evaluation/local-store-transport.d.ts +68 -0
  154. package/dist/runtime/evaluation/local-store-transport.js +18 -0
  155. package/dist/runtime/evaluation/residence-transport.d.ts +15 -0
  156. package/dist/runtime/evaluation/residence-transport.js +2 -0
  157. package/dist/runtime/evaluation/routed-persistence.d.ts +9 -0
  158. package/dist/runtime/evaluation/routed-persistence.js +2 -0
  159. package/dist/runtime/events/identity/routing.d.ts +84 -0
  160. package/dist/runtime/events/identity/routing.js +2 -0
  161. package/dist/runtime/time/TickScheduler.d.ts +77 -0
  162. package/dist/runtime/time/TickScheduler.js +3 -0
  163. package/dist/runtime/time/cron.d.ts +25 -0
  164. package/dist/runtime/time/cron.js +2 -0
  165. package/dist/runtime/time/duration.d.ts +20 -0
  166. package/dist/runtime/time/duration.js +2 -0
  167. package/dist/runtime/traits/OrbitalTraitParsing.d.ts +34 -0
  168. package/dist/runtime/traits/OrbitalTraitParsing.js +2 -0
  169. package/dist/runtime/traits/listen-targets.d.ts +16 -0
  170. package/dist/runtime/traits/listen-targets.js +3 -0
  171. package/dist/runtime/traits/trait-index.d.ts +35 -0
  172. package/dist/runtime/traits/trait-index.js +3 -0
  173. package/dist/runtime/types.d.ts +574 -0
  174. package/dist/runtime/types.js +2 -0
  175. package/dist/service/default-call-service.d.ts +23 -0
  176. package/dist/service/default-call-service.js +3 -0
  177. package/dist/service/service-provider.d.ts +37 -0
  178. package/dist/service/service-provider.js +2 -0
  179. package/package.json +46 -3
  180. package/README.md +0 -3
@@ -0,0 +1,173 @@
1
+ import { parseCron, cronMinuteKey, cronMatches } from './chunk-CROS2GEX.js';
2
+
3
+ // src/runtime/time/TickScheduler.ts
4
+ var EVERY_PASS = 0;
5
+ var CRON_CHECK_INTERVAL_MS = 1e3;
6
+ var TickScheduler = class {
7
+ ticks = /* @__PURE__ */ new Map();
8
+ nextId = 1;
9
+ running = false;
10
+ paused = false;
11
+ frameHandle = null;
12
+ hasRaf;
13
+ constructor() {
14
+ this.hasRaf = typeof requestAnimationFrame === "function" && typeof cancelAnimationFrame === "function";
15
+ }
16
+ /**
17
+ * Register a tick. `intervalMs <= 0` means "every pass" (a frame-interval
18
+ * tick, matching LOLO's `interval: "frame"`). Returns a handle to stop
19
+ * just this tick.
20
+ */
21
+ add(intervalMs, onDue) {
22
+ const id = this.nextId++;
23
+ this.ticks.set(id, {
24
+ kind: "interval",
25
+ intervalMs: intervalMs > 0 ? intervalMs : EVERY_PASS,
26
+ accumulatedMs: 0,
27
+ lastTimestamp: null,
28
+ onDue
29
+ });
30
+ this.ensureRunning();
31
+ return {
32
+ stop: () => {
33
+ this.ticks.delete(id);
34
+ this.maybeStop();
35
+ }
36
+ };
37
+ }
38
+ /**
39
+ * Register a cron-scheduled tick (`ticks { ... every "0 9 * * *" }`).
40
+ * Fires at most once per matching calendar minute. Throws if `expression` isn't a valid 5-field cron
41
+ * string; callers on a path the compiler has already validated (e.g.
42
+ * generated code) won't hit this, but a raw runtime value might.
43
+ */
44
+ addCron(expression, onDue) {
45
+ const fields = parseCron(expression);
46
+ const id = this.nextId++;
47
+ this.ticks.set(id, {
48
+ kind: "cron",
49
+ fields,
50
+ accumulatedMs: CRON_CHECK_INTERVAL_MS,
51
+ // check on the first pass, not after a full second
52
+ lastTimestamp: null,
53
+ lastFiredMinuteKey: -1,
54
+ onDue
55
+ });
56
+ this.ensureRunning();
57
+ return {
58
+ stop: () => {
59
+ this.ticks.delete(id);
60
+ this.maybeStop();
61
+ }
62
+ };
63
+ }
64
+ /** Stop every tick and the shared loop. */
65
+ stopAll() {
66
+ this.ticks.clear();
67
+ this.maybeStop();
68
+ }
69
+ /** True while the scheduler is paused (no tick callback fires and no backlog accumulates). */
70
+ get isPaused() {
71
+ return this.paused;
72
+ }
73
+ /**
74
+ * Halt the shared loop without dropping registered ticks. No `onDue`
75
+ * fires again until `resume()`. A no-op if already paused.
76
+ */
77
+ pause() {
78
+ if (this.paused) return;
79
+ this.paused = true;
80
+ this.stopLoop();
81
+ }
82
+ /**
83
+ * Restart the shared loop after `pause()`. Every registered tick's phase
84
+ * is reset (`lastTimestamp = null`) so the paused duration is never
85
+ * counted as elapsed time — the same "first pass just captures the
86
+ * timestamp" behavior a freshly `add()`-ed tick gets, which is what keeps
87
+ * resume from bursting a backlog of missed beats. A no-op if not paused.
88
+ */
89
+ resume() {
90
+ if (!this.paused) return;
91
+ this.paused = false;
92
+ for (const tick of this.ticks.values()) {
93
+ tick.lastTimestamp = null;
94
+ }
95
+ if (this.ticks.size > 0) {
96
+ this.ensureRunning();
97
+ }
98
+ }
99
+ ensureRunning() {
100
+ if (this.running || this.paused) return;
101
+ this.running = true;
102
+ if (this.hasRaf) {
103
+ const loop = (timestamp) => {
104
+ if (!this.running) return;
105
+ this.advance(timestamp);
106
+ this.frameHandle = requestAnimationFrame(loop);
107
+ };
108
+ this.frameHandle = requestAnimationFrame(loop);
109
+ } else {
110
+ this.frameHandle = setInterval(() => this.advance(Date.now()), 16);
111
+ }
112
+ }
113
+ maybeStop() {
114
+ if (this.ticks.size > 0 || !this.running) return;
115
+ this.stopLoop();
116
+ }
117
+ /** Cancel the active rAF/interval loop, if any, and clear the running flag. */
118
+ stopLoop() {
119
+ this.running = false;
120
+ if (this.frameHandle !== null) {
121
+ if (this.hasRaf) {
122
+ cancelAnimationFrame(this.frameHandle);
123
+ } else {
124
+ clearInterval(this.frameHandle);
125
+ }
126
+ this.frameHandle = null;
127
+ }
128
+ }
129
+ advance(timestamp) {
130
+ for (const tick of this.ticks.values()) {
131
+ if (tick.kind === "cron") {
132
+ this.advanceCron(tick, timestamp);
133
+ continue;
134
+ }
135
+ if (tick.intervalMs <= EVERY_PASS) {
136
+ tick.onDue();
137
+ continue;
138
+ }
139
+ if (tick.lastTimestamp === null) {
140
+ tick.lastTimestamp = timestamp;
141
+ continue;
142
+ }
143
+ tick.accumulatedMs += timestamp - tick.lastTimestamp;
144
+ tick.lastTimestamp = timestamp;
145
+ if (tick.accumulatedMs >= tick.intervalMs) {
146
+ tick.onDue();
147
+ tick.accumulatedMs = tick.accumulatedMs % tick.intervalMs;
148
+ }
149
+ }
150
+ }
151
+ /** Cron ticks check calendar-match at most once a second, deduped per matching minute. */
152
+ advanceCron(tick, timestamp) {
153
+ if (tick.lastTimestamp === null) {
154
+ tick.lastTimestamp = timestamp;
155
+ return;
156
+ }
157
+ tick.accumulatedMs += timestamp - tick.lastTimestamp;
158
+ tick.lastTimestamp = timestamp;
159
+ if (tick.accumulatedMs < CRON_CHECK_INTERVAL_MS) return;
160
+ tick.accumulatedMs = 0;
161
+ const now = /* @__PURE__ */ new Date();
162
+ const minuteKey = cronMinuteKey(now);
163
+ if (minuteKey !== tick.lastFiredMinuteKey && cronMatches(tick.fields, now)) {
164
+ tick.lastFiredMinuteKey = minuteKey;
165
+ tick.onDue();
166
+ }
167
+ }
168
+ };
169
+ function createTickScheduler() {
170
+ return new TickScheduler();
171
+ }
172
+
173
+ export { TickScheduler, createTickScheduler };
@@ -0,0 +1,49 @@
1
+ // src/runtime/time/cron.ts
2
+ function parseCronField(field, min, max) {
3
+ const values = /* @__PURE__ */ new Set();
4
+ for (const part of field.split(",")) {
5
+ if (part === "*") {
6
+ for (let i = min; i <= max; i++) values.add(i);
7
+ } else if (part.includes("/")) {
8
+ const [range, stepStr] = part.split("/");
9
+ const step = parseInt(stepStr, 10);
10
+ const start = range === "*" ? min : parseInt(range, 10);
11
+ for (let i = start; i <= max; i += step) values.add(i);
12
+ } else if (part.includes("-")) {
13
+ const [lo, hi] = part.split("-").map(Number);
14
+ for (let i = lo; i <= hi; i++) values.add(i);
15
+ } else {
16
+ values.add(parseInt(part, 10));
17
+ }
18
+ }
19
+ return values;
20
+ }
21
+ function parseCron(expression) {
22
+ const parts = expression.trim().split(/\s+/);
23
+ if (parts.length !== 5) {
24
+ throw new Error(`Invalid cron expression (expected 5 fields): ${expression}`);
25
+ }
26
+ return {
27
+ minute: parseCronField(parts[0], 0, 59),
28
+ hour: parseCronField(parts[1], 0, 23),
29
+ day: parseCronField(parts[2], 1, 31),
30
+ month: parseCronField(parts[3], 1, 12),
31
+ weekday: parseCronField(parts[4], 0, 6)
32
+ };
33
+ }
34
+ function isValidCronExpression(expression) {
35
+ try {
36
+ parseCron(expression);
37
+ return true;
38
+ } catch {
39
+ return false;
40
+ }
41
+ }
42
+ function cronMatches(fields, date) {
43
+ return fields.minute.has(date.getMinutes()) && fields.hour.has(date.getHours()) && fields.day.has(date.getDate()) && fields.month.has(date.getMonth() + 1) && fields.weekday.has(date.getDay());
44
+ }
45
+ function cronMinuteKey(date) {
46
+ return date.getFullYear() * 1e8 + date.getMonth() * 1e6 + date.getDate() * 1e4 + date.getHours() * 100 + date.getMinutes();
47
+ }
48
+
49
+ export { cronMatches, cronMinuteKey, isValidCronExpression, parseCron, parseCronField };
@@ -0,0 +1,217 @@
1
+ import { declareCapabilities, answerRequest } from './chunk-NATGCIVM.js';
2
+ import { DispatchLane } from './chunk-53IPZXDP.js';
3
+ import { decodeResolveProgress, decodeProgress } from './chunk-RBUMDYFS.js';
4
+ import { installKernelEvaluator, MountSeedSchema, EventStreamItemSchema, isEntityReferenceAny } from '@almadar/core';
5
+
6
+ function declaredEntityTypes(schema) {
7
+ return schema.orbitals.flatMap((orbital) => [orbital.entity, ...orbital.auxiliaryEntities ?? []].flatMap((entity) => isEntityReferenceAny(entity) ? [] : [entity.name]));
8
+ }
9
+ var KernelEngine = class _KernelEngine {
10
+ constructor(kernel, schemaJson, ports, entityTypes = [], forkedFrom, providedHandle, preserveProgramContext = false) {
11
+ this.kernel = kernel;
12
+ this.schemaJson = schemaJson;
13
+ this.ports = ports;
14
+ this.entityTypes = entityTypes;
15
+ this.preserveProgramContext = preserveProgramContext;
16
+ installKernelEvaluator(kernel);
17
+ this.ports = {
18
+ ...ports,
19
+ substrateMessage: (site, key, value) => JSON.parse(kernel.osCompletionEvent(JSON.stringify({ ...site, emit: { success: site.emit.onMessage } }), key, JSON.stringify({ ok: value }))),
20
+ substrateCompletion: (site, key, op, args, outcome) => JSON.parse(kernel.substrateCompletionEvent(JSON.stringify(site), key, op, JSON.stringify(args), JSON.stringify(outcome)))
21
+ };
22
+ this.handle = providedHandle ?? forkedFrom?.fork() ?? this.openServer();
23
+ this.declare();
24
+ }
25
+ kernel;
26
+ schemaJson;
27
+ ports;
28
+ entityTypes;
29
+ preserveProgramContext;
30
+ handle;
31
+ lane = new DispatchLane();
32
+ tickMount = [];
33
+ messages = { locales: [], catalogs: {} };
34
+ /**
35
+ * A kernel on the same program, registered once, holding none of this one's session: every trait at its initial
36
+ * state, no rows the kernel held, its locales and mounted ticks kept. A stateless host serves each request a
37
+ * fork (EXEC-DIST-5), `ports` answering it.
38
+ */
39
+ fork(ports = this.ports) {
40
+ const forked = new _KernelEngine(this.kernel, this.schemaJson, ports, this.entityTypes, this.handle, void 0, this.preserveProgramContext);
41
+ forked.tickMount = this.tickMount;
42
+ forked.messages = this.messages;
43
+ return forked;
44
+ }
45
+ forkProgram(program, ports = this.ports) {
46
+ const schemaJson = JSON.stringify(program);
47
+ const entityTypes = [.../* @__PURE__ */ new Set([
48
+ ...this.entityTypes,
49
+ ...declaredEntityTypes(program)
50
+ ])];
51
+ return new _KernelEngine(this.kernel, schemaJson, ports, entityTypes, void 0, this.handle.forkProgram(schemaJson), true);
52
+ }
53
+ /** A server over the host's ports. */
54
+ openServer() {
55
+ return new this.kernel.Server(this.schemaJson);
56
+ }
57
+ declare() {
58
+ const handle = this.handle;
59
+ declareCapabilities(this.ports.persistence, (entityType, queryJson, writeJson) => handle.setCapabilities(entityType, queryJson, writeJson), this.entityTypes);
60
+ }
61
+ static open(kernel, schema, ports) {
62
+ return new _KernelEngine(kernel, JSON.stringify(schema), ports, declaredEntityTypes(schema));
63
+ }
64
+ /** A fresh kernel on the same program: every trait back at its initial state. */
65
+ reset() {
66
+ const contextualHandle = this.preserveProgramContext ? this.handle.fork() : void 0;
67
+ this.handle.free();
68
+ this.handle = contextualHandle ?? this.openServer();
69
+ this.declare();
70
+ this.handle.setTickMount(JSON.stringify(this.tickMount));
71
+ this.handle.setMessages(JSON.stringify(this.messages.locales), JSON.stringify(this.messages.catalogs));
72
+ if (this.managedProgram !== void 0) this.handle.setManagedProgram(this.managedProgram);
73
+ }
74
+ managedProgram;
75
+ /** The program whose declared credentials this kernel's credential effects manage beside its own (a console's app). */
76
+ setManagedProgram(program) {
77
+ this.managedProgram = JSON.stringify(program);
78
+ this.handle.setManagedProgram(this.managedProgram);
79
+ }
80
+ /** The program's declared locales and merged catalogs (a lazy page's behavior contributes its namespaced keys). */
81
+ setMessages(locales, catalogs) {
82
+ this.messages = { locales, catalogs };
83
+ this.handle.setMessages(JSON.stringify(locales), JSON.stringify(catalogs));
84
+ }
85
+ /** The traits some client has mounted: a tick that is not `[background]` fires only for these. */
86
+ setTickMount(traits) {
87
+ this.tickMount = traits;
88
+ this.handle.setTickMount(JSON.stringify(traits));
89
+ }
90
+ /** What this host mounts when it starts: the kernel's boot mounts for the side it serves (EXEC-ENDPOINT-3). */
91
+ bootMounts(side) {
92
+ return MountSeedSchema.array().parse(JSON.parse(this.handle.bootMounts(side)));
93
+ }
94
+ /** What mounts when a client opens `routePath` (EXEC-ENDPOINT-3). */
95
+ routeMounts(routePath) {
96
+ return MountSeedSchema.array().parse(JSON.parse(this.handle.routeMounts(routePath)));
97
+ }
98
+ /** Mock-seed the `[runtime]` entities the kernel holds itself, owner columns stamped with `viewerId`. */
99
+ seedRuntimeMock(viewerId) {
100
+ this.handle.seedRuntimeMock(viewerId);
101
+ }
102
+ dispose() {
103
+ this.handle.free();
104
+ }
105
+ liveItemDelivered(item, viewer, clientId) {
106
+ return this.handle.liveItemDelivered(JSON.stringify(item), JSON.stringify(viewer), clientId);
107
+ }
108
+ /** `OsGrants::allows` over the grants of the program this kernel runs. */
109
+ osAllows(requirementJson) {
110
+ return this.handle.osAllows(requirementJson);
111
+ }
112
+ /** Place the program's anchored grants and arguments under the host's anchors. */
113
+ setOsAnchors(anchorsJson) {
114
+ return this.handle.setOsAnchors(anchorsJson);
115
+ }
116
+ /** An `os/` call's arguments with its anchored parts resolved under the host's anchors. */
117
+ osResolveArgs(op, argsJson) {
118
+ return this.handle.osResolveArgs(op, argsJson);
119
+ }
120
+ /** The current state of a trait instance, `''` when it has none. */
121
+ stateOf(traitName, entityId) {
122
+ return this.handle.getState(traitName, entityId);
123
+ }
124
+ /**
125
+ * `reference` resolved for `scope.user` against the host-answered store (EXEC-RENDER-3): the rows that still
126
+ * match its query and the viewer's `@read` in the reference's order, a missing or hidden row omitted, and `total`
127
+ * re-counted (a `total: none` reference is not re-counted: the total is the resolved window). Each read the
128
+ * kernel makes is answered here, as in `evaluate`.
129
+ */
130
+ async resolveRows(reference, scope) {
131
+ const handle = this.handle;
132
+ let progress = decodeResolveProgress(handle.resolveRows(JSON.stringify(reference), JSON.stringify(scope.user ?? null)));
133
+ while (progress.status === "await") {
134
+ const { flight, requests } = progress;
135
+ let replies;
136
+ try {
137
+ replies = await Promise.all(
138
+ requests.map(async (pending) => ({ id: pending.id, answer: await answerRequest(this.ports, pending.request, scope) }))
139
+ );
140
+ } catch (error) {
141
+ handle.abandon(flight);
142
+ throw error;
143
+ }
144
+ progress = decodeResolveProgress(handle.resumeResolve(flight, JSON.stringify(replies)));
145
+ }
146
+ return { rows: progress.rows, total: progress.total };
147
+ }
148
+ /** Run `request` against `orbital`: start it, answer every request the kernel asks for, resume until done. */
149
+ async evaluate(orbital, request, scope) {
150
+ const guard = await this.lane.enter();
151
+ try {
152
+ const handle = this.handle;
153
+ let progress = decodeProgress(handle.start(orbital, JSON.stringify(request)));
154
+ while (progress.status === "await") {
155
+ const { flight, requests } = progress;
156
+ let replies;
157
+ guard.park(request.event);
158
+ try {
159
+ replies = await Promise.all(
160
+ requests.map(async (pending) => ({ id: pending.id, answer: await answerRequest(this.ports, pending.request, { ...scope, assertReentry: () => this.lane.assertCanReenter(guard.turn) }) }))
161
+ );
162
+ } catch (error) {
163
+ handle.abandon(flight);
164
+ throw error;
165
+ }
166
+ guard.resume();
167
+ progress = decodeProgress(handle.resume(flight, JSON.stringify(replies)));
168
+ }
169
+ if (scope.onStream !== void 0) {
170
+ const items = EventStreamItemSchema.array().parse(JSON.parse(handle.dispatchStream()));
171
+ for (const item of items) scope.onStream(item);
172
+ }
173
+ return progress.response;
174
+ } finally {
175
+ guard.release();
176
+ }
177
+ }
178
+ };
179
+ function kernelRequest(request, user, locale) {
180
+ const { _activeTraits: _dropped, _targetTrait: sidecarTarget, ...rest } = request.payload ?? {};
181
+ const payload = rest;
182
+ const target = request.targetTrait ?? (typeof sidecarTarget === "string" ? sidecarTarget : void 0);
183
+ const { payload: _payload, ...others } = request;
184
+ return {
185
+ ...others,
186
+ ...request.payload !== void 0 ? { payload } : {},
187
+ ...target !== void 0 ? { targetTrait: target } : {},
188
+ ...user !== void 0 ? { user } : {},
189
+ ...locale !== void 0 ? { locale } : {}
190
+ };
191
+ }
192
+ function withOriginClient(response, clientId) {
193
+ if (clientId === void 0) return response;
194
+ return {
195
+ ...response,
196
+ emittedEvents: response.emittedEvents.map((emitted) => emitted.source === void 0 ? emitted : { ...emitted, source: { ...emitted.source, originClientId: emitted.source.originClientId ?? clientId } })
197
+ };
198
+ }
199
+ async function evaluateOnKernel(kernel, request, user, locale) {
200
+ return withOriginClient(await kernel.engine.evaluate(kernel.orbital, kernelRequest(request, user, locale), {
201
+ orbital: kernel.orbital,
202
+ ...user !== void 0 ? { user } : {},
203
+ ...request.clientId !== void 0 ? { originClientId: request.clientId } : {},
204
+ ...locale !== void 0 ? { locale } : {},
205
+ ...request.entityId !== void 0 ? { entityId: request.entityId } : {},
206
+ ...kernel.onStream !== void 0 ? { onStream: (item) => {
207
+ if (item.type === "event" && item.data.source !== void 0 && request.clientId !== void 0) {
208
+ kernel.onStream?.({ ...item, data: { ...item.data, source: { ...item.data.source, originClientId: item.data.source.originClientId ?? request.clientId } } });
209
+ } else if (item.type === "complete") {
210
+ kernel.onStream?.({ ...item, data: withOriginClient(item.data, request.clientId) });
211
+ } else kernel.onStream?.(item);
212
+ } } : {},
213
+ ...kernel.outsideEventQueue !== void 0 ? { outsideEventQueue: kernel.outsideEventQueue } : {}
214
+ }), request.clientId);
215
+ }
216
+
217
+ export { KernelEngine, evaluateOnKernel, kernelRequest };
@@ -0,0 +1,232 @@
1
+ import { createHttpTransport } from './chunk-QELZ6TEJ.js';
2
+ import { createLogger } from '@almadar/logger';
3
+
4
+ var log = createLogger("almadar:runtime:server-bridge");
5
+ var ServerBridge = class {
6
+ config;
7
+ state = {
8
+ connected: false,
9
+ eventsForwarded: 0,
10
+ eventsReceived: 0
11
+ };
12
+ unsubscribes = [];
13
+ pollTimer;
14
+ ws;
15
+ fetchFn;
16
+ /** The one HTTP request/response leg — shared with `@almadar/ui`'s transport (P5, EventTransport.ts). */
17
+ transport;
18
+ constructor(config) {
19
+ this.config = {
20
+ pollInterval: 5e3,
21
+ ...config
22
+ };
23
+ this.fetchFn = config.fetch || fetch.bind(globalThis);
24
+ this.transport = createHttpTransport({ serverUrl: config.serverUrl, fetch: this.fetchFn });
25
+ }
26
+ // ==========================================================================
27
+ // Connection Management
28
+ // ==========================================================================
29
+ /**
30
+ * Connect the bridge - start forwarding events
31
+ */
32
+ connect() {
33
+ if (this.state.connected) {
34
+ this.log("warn", "Already connected");
35
+ return;
36
+ }
37
+ this.log("info", "Connecting bridge...");
38
+ this.setupEventForwarding();
39
+ if (this.config.useWebSocket) {
40
+ this.setupWebSocket();
41
+ } else if (this.config.pollInterval && this.config.pollInterval > 0) {
42
+ this.setupPolling();
43
+ }
44
+ this.state.connected = true;
45
+ this.log("info", "Bridge connected");
46
+ }
47
+ /**
48
+ * Disconnect the bridge
49
+ */
50
+ disconnect() {
51
+ for (const unsub of this.unsubscribes) {
52
+ unsub();
53
+ }
54
+ this.unsubscribes = [];
55
+ if (this.pollTimer) {
56
+ clearInterval(this.pollTimer);
57
+ this.pollTimer = void 0;
58
+ }
59
+ if (this.ws) {
60
+ this.ws.close();
61
+ this.ws = void 0;
62
+ }
63
+ this.state.connected = false;
64
+ this.log("info", "Bridge disconnected");
65
+ }
66
+ /**
67
+ * Get current bridge state
68
+ */
69
+ getState() {
70
+ return { ...this.state };
71
+ }
72
+ // ==========================================================================
73
+ // Event Forwarding (Client -> Server)
74
+ // ==========================================================================
75
+ setupEventForwarding() {
76
+ const { eventBus, forwardEvents, forwardPattern } = this.config;
77
+ if (forwardEvents && forwardEvents.length > 0) {
78
+ for (const eventType of forwardEvents) {
79
+ const unsub = eventBus.on(eventType, (event) => {
80
+ this.forwardToServer(event);
81
+ });
82
+ this.unsubscribes.push(unsub);
83
+ }
84
+ } else if (forwardPattern) {
85
+ if (forwardPattern === "*") {
86
+ const unsub = eventBus.on("*", (event) => {
87
+ if (!event.type.startsWith("UI:") && !event.type.startsWith("BRIDGE:")) {
88
+ this.forwardToServer(event);
89
+ }
90
+ });
91
+ this.unsubscribes.push(unsub);
92
+ } else {
93
+ const prefix = forwardPattern.replace("*", "");
94
+ const unsub = eventBus.on("*", (event) => {
95
+ if (event.type.startsWith(prefix)) {
96
+ this.forwardToServer(event);
97
+ }
98
+ });
99
+ this.unsubscribes.push(unsub);
100
+ }
101
+ }
102
+ }
103
+ async forwardToServer(event) {
104
+ const { serverUrl, targetOrbital } = this.config;
105
+ try {
106
+ if (targetOrbital && targetOrbital !== "broadcast") {
107
+ await this.sendEventToOrbital(targetOrbital, event);
108
+ } else {
109
+ const orbitals = await this.fetchOrbitals();
110
+ for (const orbital of orbitals) {
111
+ await this.sendEventToOrbital(orbital.name, event);
112
+ }
113
+ }
114
+ this.state.eventsForwarded++;
115
+ this.log("debug", `Forwarded event: ${event.type}`);
116
+ } catch (error) {
117
+ this.state.lastError = String(error);
118
+ this.log("error", `Failed to forward event: ${event.type}`, error instanceof Error ? error : String(error));
119
+ }
120
+ }
121
+ async sendEventToOrbital(orbitalName, event) {
122
+ const request = { event: event.type, payload: event.payload };
123
+ const result = await this.transport.send(orbitalName, request);
124
+ if (result.emittedEvents && result.emittedEvents.length > 0) {
125
+ for (const emitted of result.emittedEvents) {
126
+ this.config.eventBus.emit(`SERVER:${emitted.event}`, emitted.payload);
127
+ this.state.eventsReceived++;
128
+ }
129
+ }
130
+ }
131
+ async fetchOrbitals() {
132
+ const response = await this.fetchFn(this.config.serverUrl);
133
+ if (!response.ok) {
134
+ throw new Error(`Failed to fetch orbitals: ${response.status}`);
135
+ }
136
+ const data = await response.json();
137
+ return data.orbitals || [];
138
+ }
139
+ // ==========================================================================
140
+ // Server -> Client (Polling)
141
+ // ==========================================================================
142
+ setupPolling() {
143
+ this.log("debug", "Polling mode - events received via forward response");
144
+ }
145
+ // ==========================================================================
146
+ // Server -> Client (WebSocket)
147
+ // ==========================================================================
148
+ setupWebSocket() {
149
+ const { serverUrl } = this.config;
150
+ const wsUrl = serverUrl.replace(/^http:/, "ws:").replace(/^https:/, "wss:").replace(/\/api\/orbitals$/, "/ws/orbitals");
151
+ try {
152
+ this.ws = new WebSocket(wsUrl);
153
+ this.ws.onopen = () => {
154
+ this.log("info", "WebSocket connected");
155
+ };
156
+ this.ws.onmessage = (msg) => {
157
+ try {
158
+ const data = JSON.parse(msg.data);
159
+ if (data.type === "event") {
160
+ this.config.eventBus.emit(`SERVER:${data.event}`, data.payload);
161
+ this.state.eventsReceived++;
162
+ this.log("debug", `Received server event: ${data.event}`);
163
+ }
164
+ } catch (error) {
165
+ this.log("error", "Failed to parse WebSocket message", error instanceof Error ? error : String(error));
166
+ }
167
+ };
168
+ this.ws.onerror = (error) => {
169
+ this.state.lastError = "WebSocket error";
170
+ this.log("error", "WebSocket error", error);
171
+ };
172
+ this.ws.onclose = () => {
173
+ this.log("info", "WebSocket closed");
174
+ };
175
+ } catch (error) {
176
+ this.log("error", "Failed to create WebSocket", error instanceof Error ? error : String(error));
177
+ }
178
+ }
179
+ // ==========================================================================
180
+ // Direct Methods
181
+ // ==========================================================================
182
+ /**
183
+ * Send an event directly to a specific orbital (bypassing EventBus)
184
+ */
185
+ async sendEvent(orbitalName, event, payload) {
186
+ try {
187
+ const result = await this.transport.send(orbitalName, { event, payload });
188
+ return {
189
+ success: result.success,
190
+ states: result.states,
191
+ emittedEvents: result.emittedEvents,
192
+ error: result.error
193
+ };
194
+ } catch (error) {
195
+ return { success: false, error: String(error) };
196
+ }
197
+ }
198
+ /**
199
+ * Get current state of an orbital's traits
200
+ */
201
+ async getOrbitalState(orbitalName) {
202
+ const { serverUrl } = this.config;
203
+ const url = `${serverUrl}/${orbitalName}`;
204
+ try {
205
+ const response = await this.fetchFn(url);
206
+ const data = await response.json();
207
+ if (data.success && data.orbital) {
208
+ const states = {};
209
+ for (const trait of data.orbital.traits) {
210
+ states[trait.name] = trait.currentState;
211
+ }
212
+ return { success: true, states };
213
+ }
214
+ return { success: false, error: data.error };
215
+ } catch (error) {
216
+ return { success: false, error: String(error) };
217
+ }
218
+ }
219
+ // ==========================================================================
220
+ // Utilities
221
+ // ==========================================================================
222
+ log(level, message, data) {
223
+ if (!this.config.debug && level === "debug") return;
224
+ const meta = data === void 0 ? {} : data instanceof Error ? { error: data } : { detail: typeof data === "string" ? data : JSON.stringify(data) };
225
+ log[level](message, meta);
226
+ }
227
+ };
228
+ function createServerBridge(config) {
229
+ return new ServerBridge(config);
230
+ }
231
+
232
+ export { ServerBridge, createServerBridge };
@@ -0,0 +1,25 @@
1
+ import { findExternalInput } from '@almadar/core';
2
+
3
+ // src/client/declared-input-dispatch.ts
4
+ async function dispatchDeclaredInput(schema, orbitalName, request, send) {
5
+ const declared = schema ? findExternalInput(schema, orbitalName, request.targetTrait, request.event) : void 0;
6
+ if (!declared) {
7
+ return {
8
+ success: false,
9
+ transitioned: false,
10
+ states: {},
11
+ emittedEvents: [],
12
+ error: `'${request.event}' is not an external input of ${orbitalName}.${request.targetTrait}`,
13
+ rejections: [{ code: "not-an-external-input", trait: request.targetTrait, event: request.event }]
14
+ };
15
+ }
16
+ return send(orbitalName, {
17
+ event: request.event,
18
+ payload: request.payload ?? {},
19
+ targetTrait: request.targetTrait,
20
+ ...request.user !== void 0 ? { user: request.user } : {},
21
+ ...request.entityId !== void 0 ? { entityId: request.entityId } : {}
22
+ });
23
+ }
24
+
25
+ export { dispatchDeclaredInput };