@almadar/shell-runtime 0.0.0-stage → 1.0.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 (127) hide show
  1. package/dist/chunk-4SEK5ER2.js +8 -0
  2. package/dist/chunk-57SBCUSM.js +62 -0
  3. package/dist/chunk-625EP7W2.js +108 -0
  4. package/dist/chunk-64IH2SMY.js +22 -0
  5. package/dist/chunk-7SDV3QWR.js +67 -0
  6. package/dist/chunk-7WB6VWRK.js +798 -0
  7. package/dist/chunk-A2JJT4GA.js +56 -0
  8. package/dist/chunk-BAS26HBN.js +10 -0
  9. package/dist/chunk-BOVU4TCQ.js +65 -0
  10. package/dist/chunk-BY5G4225.js +6 -0
  11. package/dist/chunk-DFDNHVDW.js +284 -0
  12. package/dist/chunk-ELBLT2HZ.js +1 -0
  13. package/dist/chunk-EZZYLTWY.js +1 -0
  14. package/dist/chunk-FWA5K7Q2.js +48 -0
  15. package/dist/chunk-HC6ERSAH.js +55 -0
  16. package/dist/chunk-HLOUDLPI.js +48 -0
  17. package/dist/chunk-I3TKINSO.js +398 -0
  18. package/dist/chunk-IOPUS6Q5.js +15 -0
  19. package/dist/chunk-LKWYCYX5.js +22 -0
  20. package/dist/chunk-LYFIQPXX.js +1 -0
  21. package/dist/chunk-LZIOTBFX.js +1 -0
  22. package/dist/chunk-MU335ZA4.js +63 -0
  23. package/dist/chunk-MZKIL5IN.js +74 -0
  24. package/dist/chunk-N7HYNKK6.js +83 -0
  25. package/dist/chunk-NTFQ36UN.js +52 -0
  26. package/dist/chunk-NU6LVKGS.js +15 -0
  27. package/dist/chunk-O57GKTYU.js +30 -0
  28. package/dist/chunk-P2N6K2IV.js +20 -0
  29. package/dist/chunk-P53CKOME.js +1 -0
  30. package/dist/chunk-QCJWPXLH.js +40 -0
  31. package/dist/chunk-SKLMWHQD.js +109 -0
  32. package/dist/chunk-SOQDEPME.js +131 -0
  33. package/dist/chunk-UBN5JJKZ.js +26 -0
  34. package/dist/chunk-UD2CXYSJ.js +244 -0
  35. package/dist/chunk-UP6GGGAN.js +34 -0
  36. package/dist/chunk-V4EMDPCO.js +33 -0
  37. package/dist/chunk-VNGXRYFF.js +9 -0
  38. package/dist/chunk-WGCWJOT2.js +21 -0
  39. package/dist/chunk-X6W44WHO.js +26 -0
  40. package/dist/chunk-XXWNMJI6.js +15 -0
  41. package/dist/chunk-ZUSM4VW4.js +197 -0
  42. package/dist/client/EventTransport.d.ts +147 -0
  43. package/dist/client/EventTransport.js +1 -0
  44. package/dist/client/awaiting.d.ts +28 -0
  45. package/dist/client/awaiting.js +1 -0
  46. package/dist/client/channel-transport.d.ts +69 -0
  47. package/dist/client/channel-transport.js +1 -0
  48. package/dist/client/circuit-store.d.ts +87 -0
  49. package/dist/client/circuit-store.js +3 -0
  50. package/dist/client/client-kernel.d.ts +134 -0
  51. package/dist/client/client-kernel.js +1 -0
  52. package/dist/client/declared-input-dispatch.d.ts +22 -0
  53. package/dist/client/declared-input-dispatch.js +1 -0
  54. package/dist/client/inbound-dispatch.d.ts +24 -0
  55. package/dist/client/inbound-dispatch.js +1 -0
  56. package/dist/client/local-fold.d.ts +33 -0
  57. package/dist/client/local-fold.js +1 -0
  58. package/dist/client/render-sigils.d.ts +24 -0
  59. package/dist/client/render-sigils.js +1 -0
  60. package/dist/client/state-book.d.ts +30 -0
  61. package/dist/client/state-book.js +1 -0
  62. package/dist/client/trait-mount-error.d.ts +9 -0
  63. package/dist/client/trait-mount-error.js +1 -0
  64. package/dist/client/trait-types.d.ts +67 -0
  65. package/dist/client/trait-types.js +1 -0
  66. package/dist/credentials/env-vars.d.ts +18 -0
  67. package/dist/credentials/env-vars.js +1 -0
  68. package/dist/credentials/scope.d.ts +51 -0
  69. package/dist/credentials/scope.js +1 -0
  70. package/dist/credentials/vault.d.ts +142 -0
  71. package/dist/credentials/vault.js +1 -0
  72. package/dist/effects/in-flight-calls.d.ts +36 -0
  73. package/dist/effects/in-flight-calls.js +1 -0
  74. package/dist/effects/on-device-model.d.ts +22 -0
  75. package/dist/effects/on-device-model.js +1 -0
  76. package/dist/events/EventBus.d.ts +87 -0
  77. package/dist/events/EventBus.js +1 -0
  78. package/dist/events/emit-stamp.d.ts +47 -0
  79. package/dist/events/emit-stamp.js +1 -0
  80. package/dist/index.d.ts +43 -0
  81. package/dist/index.js +41 -0
  82. package/dist/kernel/decode.d.ts +13 -0
  83. package/dist/kernel/decode.js +1 -0
  84. package/dist/kernel/engine.d.ts +195 -0
  85. package/dist/kernel/engine.js +9 -0
  86. package/dist/kernel/frame-applier.d.ts +44 -0
  87. package/dist/kernel/frame-applier.js +1 -0
  88. package/dist/kernel/host.d.ts +206 -0
  89. package/dist/kernel/host.js +6 -0
  90. package/dist/kernel/lane.d.ts +7 -0
  91. package/dist/kernel/lane.js +1 -0
  92. package/dist/kernel/os-fetch-port.d.ts +7 -0
  93. package/dist/kernel/os-fetch-port.js +3 -0
  94. package/dist/kernel/os-port.d.ts +155 -0
  95. package/dist/kernel/os-port.js +1 -0
  96. package/dist/kernel/program-effects.d.ts +48 -0
  97. package/dist/kernel/program-effects.js +1 -0
  98. package/dist/kernel/secret-ports.d.ts +139 -0
  99. package/dist/kernel/secret-ports.js +2 -0
  100. package/dist/kernel/wire.d.ts +104 -0
  101. package/dist/kernel/wire.js +1 -0
  102. package/dist/loader/fetch-loader.d.ts +26 -0
  103. package/dist/loader/fetch-loader.js +1 -0
  104. package/dist/loader/lazy-page.d.ts +12 -0
  105. package/dist/loader/lazy-page.js +1 -0
  106. package/dist/loader/schema-loader.d.ts +144 -0
  107. package/dist/loader/schema-loader.js +1 -0
  108. package/dist/persistence/native-memory.d.ts +28 -0
  109. package/dist/persistence/native-memory.js +1 -0
  110. package/dist/runtime/entities/declaredCollections.d.ts +6 -0
  111. package/dist/runtime/entities/declaredCollections.js +1 -0
  112. package/dist/runtime/evaluation/local-store-transport.d.ts +59 -0
  113. package/dist/runtime/evaluation/local-store-transport.js +14 -0
  114. package/dist/runtime/evaluation/residence-transport.d.ts +15 -0
  115. package/dist/runtime/evaluation/residence-transport.js +1 -0
  116. package/dist/runtime/evaluation/routed-persistence.d.ts +9 -0
  117. package/dist/runtime/evaluation/routed-persistence.js +1 -0
  118. package/dist/runtime/events/identity/routing.d.ts +33 -0
  119. package/dist/runtime/events/identity/routing.js +1 -0
  120. package/dist/runtime/types.d.ts +374 -0
  121. package/dist/runtime/types.js +1 -0
  122. package/dist/service/default-call-service.d.ts +23 -0
  123. package/dist/service/default-call-service.js +2 -0
  124. package/dist/service/service-provider.d.ts +37 -0
  125. package/dist/service/service-provider.js +1 -0
  126. package/package.json +49 -3
  127. package/README.md +0 -3
@@ -0,0 +1,56 @@
1
+ // src/client/state-book.ts
2
+ function findInitialState(trait) {
3
+ const markedInitial = trait.states?.find((s) => s.isInitial)?.name;
4
+ return markedInitial ?? trait.states?.[0]?.name ?? "unknown";
5
+ }
6
+ function createInitialTraitState(trait) {
7
+ return {
8
+ traitName: trait.name,
9
+ currentState: findInitialState(trait),
10
+ previousState: null,
11
+ lastEvent: null,
12
+ context: {}
13
+ };
14
+ }
15
+ var SINGLETON_SCOPE = "__singleton__";
16
+ var key = (traitName, entityId) => `${traitName}::${entityId ?? SINGLETON_SCOPE}`;
17
+ var StateBook = class {
18
+ constructor(traits = [], observer) {
19
+ this.observer = observer;
20
+ for (const trait of traits) this.traits.set(trait.name, trait);
21
+ }
22
+ observer;
23
+ traits = /* @__PURE__ */ new Map();
24
+ states = /* @__PURE__ */ new Map();
25
+ /** The trait's state, its initial state until the kernel reports one; `undefined` for a trait not on the page. */
26
+ getState(traitName, entityId) {
27
+ const recorded = this.states.get(key(traitName, entityId));
28
+ if (recorded !== void 0) return recorded;
29
+ const trait = this.traits.get(traitName);
30
+ return trait === void 0 ? void 0 : createInitialTraitState(trait);
31
+ }
32
+ /** Each trait's singleton-scope state. */
33
+ getAllStates() {
34
+ const out = /* @__PURE__ */ new Map();
35
+ for (const name of this.traits.keys()) {
36
+ const state = this.getState(name);
37
+ if (state !== void 0) out.set(name, state);
38
+ }
39
+ return out;
40
+ }
41
+ /** Record the state the kernel reports. No-op for a trait not on the page. */
42
+ seedState(traitName, currentState, entityId) {
43
+ const trait = this.traits.get(traitName);
44
+ if (trait === void 0) return;
45
+ const existing = this.states.get(key(traitName, entityId)) ?? createInitialTraitState(trait);
46
+ this.states.set(key(traitName, entityId), { ...existing, currentState, previousState: existing.currentState });
47
+ }
48
+ /** Undo an optimistic commit, reporting the hop back to the observer. */
49
+ rollbackState(traitName, toState, entityId, cause) {
50
+ const from = this.getState(traitName, entityId)?.currentState;
51
+ this.seedState(traitName, toState, entityId);
52
+ if (from !== void 0 && from !== toState) this.observer?.onRollback?.({ traitName, from, to: toState, cause });
53
+ }
54
+ };
55
+
56
+ export { StateBook, createInitialTraitState, findInitialState };
@@ -0,0 +1,10 @@
1
+ // src/runtime/entities/declaredCollections.ts
2
+ function bindDeclaredCollections(store, entities) {
3
+ for (const entity of entities) {
4
+ if (entity.name && entity.collection && entity.browser !== true && entity.persistence !== "runtime") {
5
+ store.bindCollection?.(entity.name, entity.collection);
6
+ }
7
+ }
8
+ }
9
+
10
+ export { bindDeclaredCollections };
@@ -0,0 +1,65 @@
1
+ // src/kernel/os-port.ts
2
+ function kernelOsTls(server) {
3
+ return {
4
+ complete: (key, outcome) => {
5
+ const step = JSON.parse(server().tlsComplete(key, JSON.stringify(outcome)));
6
+ return step;
7
+ }
8
+ };
9
+ }
10
+ function kernelOsCompletions(kernel) {
11
+ const deferring = JSON.parse(kernel.osDeferringOps());
12
+ const streaming = JSON.parse(kernel.osStreamOps());
13
+ return {
14
+ defers: (op) => deferring.includes(op),
15
+ streams: (op) => streaming.includes(op),
16
+ event: (site, key, outcome) => {
17
+ const fired = JSON.parse(kernel.osCompletionEvent(JSON.stringify(site), key, JSON.stringify(outcome)));
18
+ return fired;
19
+ }
20
+ };
21
+ }
22
+ function kernelOsGrants(matchers, server) {
23
+ return {
24
+ allows: (need, orbital) => server().osAllows(JSON.stringify(need), orbital),
25
+ globMatches: (glob, path) => matchers.osGlobMatches(glob, path),
26
+ isPublicAddress: (ip) => matchers.isPublicAddress(ip),
27
+ anchor: (anchors) => {
28
+ const problems = JSON.parse(server().setOsAnchors(JSON.stringify(anchors)));
29
+ return problems;
30
+ },
31
+ resolveArgs: (op, args) => {
32
+ const resolved = JSON.parse(server().osResolveArgs(op, JSON.stringify(args)));
33
+ return resolved;
34
+ }
35
+ };
36
+ }
37
+ function expandAnchor(anchors, text) {
38
+ const home = text === "~" ? "" : text.startsWith("~/") ? text.slice(2) : void 0;
39
+ const start = text === "." ? "" : text.startsWith("./") ? text.slice(2) : void 0;
40
+ const rest = home ?? start;
41
+ if (rest === void 0) return { kind: "plain" };
42
+ const dir = home !== void 0 ? anchors.home : anchors.start;
43
+ if (dir === void 0) return { kind: "unknown", message: `${text}: this host has no ${home !== void 0 ? "home" : "start"} directory` };
44
+ return { kind: "anchored", path: rest === "" ? dir : `${dir.replace(/\/+$/, "")}/${rest}` };
45
+ }
46
+ function anchoredForms(anchors, path) {
47
+ const forms = [path];
48
+ const under = (dir, prefix) => {
49
+ if (dir === void 0) return;
50
+ const root = dir.replace(/\/+$/, "");
51
+ if (path === root) forms.push(prefix);
52
+ else if (path.startsWith(`${root}/`)) forms.push(`${prefix}${path.slice(root.length + 1)}`);
53
+ };
54
+ under(anchors.home, "~/");
55
+ under(anchors.start, "./");
56
+ return forms;
57
+ }
58
+ function anchoredGrants(grants, anchors) {
59
+ return {
60
+ ...grants,
61
+ allows: (need, orbital) => need.need === "fs" ? anchoredForms(anchors, need.path).some((path) => grants.allows({ ...need, path }, orbital)) : grants.allows(need, orbital)
62
+ };
63
+ }
64
+
65
+ export { anchoredGrants, expandAnchor, kernelOsCompletions, kernelOsGrants, kernelOsTls };
@@ -0,0 +1,6 @@
1
+ // src/runtime/events/identity/routing.ts
2
+ function eventRouteKey(eventName, eventId) {
3
+ return eventId ? `@evt:${eventId}` : eventName;
4
+ }
5
+
6
+ export { eventRouteKey };
@@ -0,0 +1,284 @@
1
+ import { isJsonObject, credentialStorageKey, EntityRowSchema } from '@almadar/core';
2
+
3
+ // src/credentials/vault.ts
4
+ var CredentialExchangeError = class extends Error {
5
+ constructor(kind, message) {
6
+ super(message);
7
+ this.kind = kind;
8
+ this.name = "CredentialExchangeError";
9
+ }
10
+ kind;
11
+ };
12
+ var FAILURE_KINDS = ["unavailable", "unsigned", "denied", "invalid_grant", "invalid_client", "transport", "malformed", "refused"];
13
+ function failureOf(value) {
14
+ if (value === void 0 || !isJsonObject(value)) throw new Error("the kernel connect failure is not an object");
15
+ const kind = FAILURE_KINDS.find((known) => known === value.kind);
16
+ if (kind === void 0) throw new Error("the kernel connect failure has an unknown kind");
17
+ return { kind, message: textOf(value.message, "failure message") };
18
+ }
19
+ function standingOf(value) {
20
+ if (value === void 0 || !isJsonObject(value) || typeof value.connected !== "boolean") throw new Error("the kernel credential standing is malformed");
21
+ return { connected: value.connected, ...typeof value.label === "string" ? { label: value.label } : {} };
22
+ }
23
+ function requestOf(value) {
24
+ if (value === void 0 || !isJsonObject(value) || !Array.isArray(value.headers)) throw new Error("the kernel token request is malformed");
25
+ const headers = value.headers.map((pair) => {
26
+ if (!Array.isArray(pair) || pair.length !== 2) throw new Error("the kernel token request header is malformed");
27
+ return [textOf(pair[0], "header name"), textOf(pair[1], "header value")];
28
+ });
29
+ return { request: { endpoint: textOf(value.endpoint, "token endpoint"), headers, body: textOf(value.body, "token body") }, ticket: numberOf(value.ticket, "exchange ticket") };
30
+ }
31
+ function parsed(json) {
32
+ return JSON.parse(json);
33
+ }
34
+ function objectOf(json, what) {
35
+ const value = parsed(json);
36
+ if (!isJsonObject(value)) throw new Error(`the kernel ${what} is not an object`);
37
+ return value;
38
+ }
39
+ function textOf(value, what) {
40
+ if (typeof value !== "string") throw new Error(`the kernel ${what} is not text`);
41
+ return value;
42
+ }
43
+ function numberOf(value, what) {
44
+ if (typeof value !== "number") throw new Error(`the kernel ${what} is not a number`);
45
+ return value;
46
+ }
47
+ function entryOf(value) {
48
+ if (!isJsonObject(value)) throw new Error("the kernel credential entry is not an object");
49
+ return {
50
+ service: textOf(value.service, "entry service"),
51
+ envVar: textOf(value.envVar, "entry name"),
52
+ last4: textOf(value.last4, "entry last4"),
53
+ updatedAt: numberOf(value.updatedAt, "entry updatedAt"),
54
+ ...value.principal !== void 0 ? { principal: textOf(value.principal, "entry principal") } : {}
55
+ };
56
+ }
57
+ function writeOf(item) {
58
+ if (!isJsonObject(item)) throw new Error("the kernel credential write is not an object");
59
+ const id = textOf(item.id, "write id");
60
+ if (item.write === "delete") return { write: "delete", id };
61
+ const data = EntityRowSchema.parse(item.data);
62
+ if (item.write === "update") return { write: "update", id, data };
63
+ if (item.write === "create") return { write: "create", id, data: { ...data, id } };
64
+ throw new Error("the kernel credential write is neither create, update nor delete");
65
+ }
66
+ var CredentialVault = class _CredentialVault {
67
+ constructor(kernel, rows) {
68
+ this.kernel = kernel;
69
+ this.rows = rows;
70
+ }
71
+ kernel;
72
+ rows;
73
+ listeners = /* @__PURE__ */ new Set();
74
+ queue = Promise.resolve();
75
+ broken = false;
76
+ /** List the host's rows into the kernel and read them; rejects naming the rows it cannot read. */
77
+ static async open(kernel, rows) {
78
+ const vault = new _CredentialVault(kernel, rows);
79
+ await vault.reload();
80
+ return vault;
81
+ }
82
+ /** True when a valid master key is present (writes and decryption enabled). */
83
+ get enabled() {
84
+ return this.kernel.enabled;
85
+ }
86
+ /** Subscribe to credential changes; returns an unsubscribe function. */
87
+ subscribe(listener) {
88
+ this.listeners.add(listener);
89
+ return () => this.listeners.delete(listener);
90
+ }
91
+ notify(principal) {
92
+ for (const listener of this.listeners) listener(principal);
93
+ }
94
+ async reload() {
95
+ try {
96
+ this.kernel.load(JSON.stringify(await this.rows.list()));
97
+ this.broken = false;
98
+ } catch (error) {
99
+ this.broken = true;
100
+ throw error;
101
+ }
102
+ }
103
+ current() {
104
+ if (this.broken) throw new Error("the credential store could not be read or written; reload it");
105
+ return this.kernel;
106
+ }
107
+ async apply() {
108
+ const writes = parsed(this.kernel.takeWrites());
109
+ if (!Array.isArray(writes)) throw new Error("the kernel credential writes are not a list");
110
+ try {
111
+ for (const item of writes) {
112
+ const write = writeOf(item);
113
+ if (write.write === "create") await this.rows.create(write.data);
114
+ else if (write.write === "update") await this.rows.update(write.id, write.data);
115
+ else await this.rows.delete(write.id);
116
+ }
117
+ } catch (error) {
118
+ this.broken = true;
119
+ throw error;
120
+ }
121
+ }
122
+ /** One operation at a time: the kernel runs it, then its writes reach the medium before the next begins. */
123
+ serial(run) {
124
+ const next = this.queue.then(run);
125
+ this.queue = next.then(
126
+ () => void 0,
127
+ () => void 0
128
+ );
129
+ return next;
130
+ }
131
+ exclusive(fresh, work, principal) {
132
+ return this.serial(async () => {
133
+ if (fresh || this.broken) await this.reload();
134
+ let result;
135
+ try {
136
+ result = work();
137
+ } catch (error) {
138
+ if (this.kernel.takeWrites() !== "[]") this.broken = true;
139
+ throw error;
140
+ }
141
+ await this.apply();
142
+ this.notify(principal);
143
+ return result;
144
+ });
145
+ }
146
+ /** Resolve one credential by name (warmed values only). */
147
+ resolve(envVar, principal) {
148
+ return this.current().resolve(envVar, principal);
149
+ }
150
+ /** The non-secret account label stored in the clear beside a credential's ciphertext; undefined when its row carries none. */
151
+ label(envVar, principal) {
152
+ return this.current().label(envVar, principal);
153
+ }
154
+ /** All app-wide values as an env-shaped map, for merging over the process env. */
155
+ snapshotEnv() {
156
+ const env = {};
157
+ for (const [name, value] of Object.entries(objectOf(this.current().snapshotEnv(), "environment"))) env[name] = textOf(value, "environment value");
158
+ return env;
159
+ }
160
+ /** Masked entries for display — never includes plaintext. */
161
+ entries(principal) {
162
+ const entries = parsed(this.current().entries(principal));
163
+ if (!Array.isArray(entries)) throw new Error("the kernel credential entries are not a list");
164
+ return entries.map(entryOf);
165
+ }
166
+ /** Whether the store can encrypt and the service and name of every app-wide row. */
167
+ listing() {
168
+ const listing = objectOf(this.current().listing(), "listing");
169
+ const entries = listing.entries;
170
+ if (!Array.isArray(entries) || typeof listing.enabled !== "boolean") throw new Error("the kernel credential listing is malformed");
171
+ return {
172
+ enabled: listing.enabled,
173
+ entries: entries.map((entry) => {
174
+ if (!isJsonObject(entry)) throw new Error("the kernel credential listing holds a row that is not an object");
175
+ const row = { service: textOf(entry.service, "listing service"), name: textOf(entry.name, "listing name") };
176
+ return entry.last4 === void 0 ? row : { ...row, last4: textOf(entry.last4, "listing last4") };
177
+ })
178
+ };
179
+ }
180
+ /** Upsert one credential; `label` is a non-secret account name kept in the clear beside it, left out it stays as it is. */
181
+ async set(service, envVar, value, principal, label) {
182
+ return entryOf(parsed(await this.exclusive(false, () => this.kernel.set(service, envVar, value, principal, label), principal)));
183
+ }
184
+ /** Replace the value of a credential already stored, keeping its service (a rotated refresh token). */
185
+ async replace(envVar, value, principal) {
186
+ return entryOf(parsed(await this.exclusive(false, () => this.kernel.replace(envVar, value, principal), principal)));
187
+ }
188
+ /** Keep `value` as the credential until it is forgotten, creating it when absent. */
189
+ async persist(envVar, value, principal, label) {
190
+ return entryOf(parsed(await this.exclusive(false, () => this.kernel.persist(envVar, value, principal, label), principal)));
191
+ }
192
+ /** Delete one credential row; whether one existed. */
193
+ remove(envVar, principal) {
194
+ return this.exclusive(false, () => this.kernel.remove(envVar, principal), principal);
195
+ }
196
+ flights = /* @__PURE__ */ new Map();
197
+ turns = /* @__PURE__ */ new Map();
198
+ async post(post, begun) {
199
+ try {
200
+ return await post(begun.request);
201
+ } catch (error) {
202
+ this.kernel.connectAbandon(begun.ticket);
203
+ throw new CredentialExchangeError("transport", error instanceof Error ? error.message : "the token endpoint could not be reached");
204
+ }
205
+ }
206
+ async finish(ticket, reply) {
207
+ const settled = objectOf(await this.exclusive(false, () => this.kernel.connectFinish(ticket, reply.status, reply.body)), "connect outcome");
208
+ if (settled.error !== void 0) {
209
+ const failure = failureOf(settled.error);
210
+ throw new CredentialExchangeError(failure.kind, failure.message);
211
+ }
212
+ return standingOf(settled.ok);
213
+ }
214
+ /** Exchange `grant` at its token endpoint through `post` and keep the result as the credential: the kernel builds the request and settles the reply, the host only sends. */
215
+ async connect(grant, post) {
216
+ try {
217
+ const run = async () => {
218
+ const begun = objectOf(this.current().connectBegin(JSON.stringify(grant)), "connect request");
219
+ if (begun.error !== void 0) {
220
+ const failure = failureOf(begun.error);
221
+ throw new CredentialExchangeError(failure.kind, failure.message);
222
+ }
223
+ const request = requestOf(begun.ok);
224
+ return this.finish(request.ticket, await this.post(post, request));
225
+ };
226
+ if (grant.grant !== "refresh_token") return { ok: await run() };
227
+ const key = credentialStorageKey({ name: grant.name, ...grant.principal !== void 0 ? { principal: grant.principal } : {} });
228
+ const previous = this.turns.get(key);
229
+ const mine = (async () => {
230
+ await previous?.catch(() => void 0);
231
+ return run();
232
+ })();
233
+ this.turns.set(key, mine);
234
+ try {
235
+ return { ok: await mine };
236
+ } finally {
237
+ if (this.turns.get(key) === mine) this.turns.delete(key);
238
+ }
239
+ } catch (error) {
240
+ if (error instanceof CredentialExchangeError) return { error: { kind: error.kind, message: error.message } };
241
+ throw error;
242
+ }
243
+ }
244
+ /** The credential `envVar` for `principal`, refreshed first through `post` when it is past its expiry; concurrent resolves share one refresh. Undefined when nothing is held, nothing can renew it, or the provider refused (which drops the row). */
245
+ async resolveFresh(envVar, principal, post) {
246
+ const begun = objectOf(this.current().refreshBegin(envVar, principal), "refresh outcome");
247
+ if (begun.error !== void 0) {
248
+ const failure = failureOf(begun.error);
249
+ throw new CredentialExchangeError(failure.kind, failure.message);
250
+ }
251
+ const ok = begun.ok;
252
+ if (!isJsonObject(ok)) throw new Error("the kernel refresh outcome is malformed");
253
+ if (ok.exchange === void 0) return typeof ok.resolved === "string" ? ok.resolved : void 0;
254
+ const key = credentialStorageKey({ name: envVar, ...principal !== void 0 ? { principal } : {} });
255
+ const running = this.flights.get(key);
256
+ if (running !== void 0) {
257
+ this.current().connectAbandon(requestOf(ok.exchange).ticket);
258
+ return running;
259
+ }
260
+ const request = requestOf(ok.exchange);
261
+ const flight = (async () => {
262
+ try {
263
+ await this.finish(request.ticket, await this.post(post, request));
264
+ } catch (error) {
265
+ if (error instanceof CredentialExchangeError && error.kind === "invalid_grant") return void 0;
266
+ throw error;
267
+ }
268
+ return this.resolve(envVar, principal);
269
+ })();
270
+ this.flights.set(key, flight);
271
+ try {
272
+ return await flight;
273
+ } finally {
274
+ this.flights.delete(key);
275
+ }
276
+ }
277
+ /** Re-encrypt every row under the current master key; a row the kernel cannot read fails it before any row changes. */
278
+ async rotate() {
279
+ const counts = objectOf(await this.exclusive(true, () => this.kernel.rotate()), "rotation");
280
+ return { rotated: numberOf(counts.rotated, "rotated"), alreadyCurrent: numberOf(counts.alreadyCurrent, "alreadyCurrent"), unreadable: numberOf(counts.unreadable, "unreadable") };
281
+ }
282
+ };
283
+
284
+ export { CredentialExchangeError, CredentialVault };
@@ -0,0 +1 @@
1
+
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,48 @@
1
+ // src/effects/on-device-model.ts
2
+ var NO_ON_DEVICE_MODEL = "browser/prompt: no on-device model is wired into this host";
3
+ var NO_ON_DEVICE_EMBEDDER = "browser/embed: no on-device embedder is wired into this host";
4
+ var OnDeviceModelFailure = class extends Error {
5
+ kind;
6
+ payload;
7
+ constructor(kind, message, key) {
8
+ super(message);
9
+ this.name = "OnDeviceModelFailure";
10
+ this.kind = kind;
11
+ this.payload = { error: message, kind, key };
12
+ }
13
+ };
14
+ function failureMessage(error) {
15
+ return error instanceof Error ? error.message : String(error);
16
+ }
17
+ async function promptOnDevice(ports, text, schemaText, key, signal) {
18
+ const model = ports?.model;
19
+ if (model === void 0) throw new OnDeviceModelFailure("unconfigured", NO_ON_DEVICE_MODEL, key);
20
+ let schema;
21
+ try {
22
+ const parsed = JSON.parse(schemaText);
23
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("not an object");
24
+ schema = parsed;
25
+ } catch {
26
+ throw new OnDeviceModelFailure("failed", "browser/prompt: the response schema must be JSON Schema text: a JSON object", key);
27
+ }
28
+ try {
29
+ const state = await model.availability();
30
+ if (state !== "available") throw new OnDeviceModelFailure("unavailable", `The on-device model is ${state}`, key);
31
+ return await model.prompt(text, schema, signal);
32
+ } catch (error) {
33
+ if (error instanceof OnDeviceModelFailure) throw error;
34
+ throw new OnDeviceModelFailure("failed", failureMessage(error), key);
35
+ }
36
+ }
37
+ async function embedOnDevice(ports, texts, key, signal) {
38
+ if (texts.length === 0) return [];
39
+ const embedder = ports?.embedder;
40
+ if (embedder === void 0) throw new OnDeviceModelFailure("unconfigured", NO_ON_DEVICE_EMBEDDER, key);
41
+ try {
42
+ return await embedder.embed([...texts], signal);
43
+ } catch (error) {
44
+ throw new OnDeviceModelFailure("failed", failureMessage(error), key);
45
+ }
46
+ }
47
+
48
+ export { NO_ON_DEVICE_EMBEDDER, NO_ON_DEVICE_MODEL, OnDeviceModelFailure, embedOnDevice, promptOnDevice };
@@ -0,0 +1,55 @@
1
+ // src/client/awaiting.ts
2
+ var AwaitingRegistry = class {
3
+ entries = /* @__PURE__ */ new Map();
4
+ listeners = /* @__PURE__ */ new Map();
5
+ anyListeners = /* @__PURE__ */ new Set();
6
+ version = 0;
7
+ begin(list) {
8
+ for (const entry of list) {
9
+ this.entries.set(entry.trait, entry);
10
+ this.emit(entry.trait);
11
+ }
12
+ }
13
+ end(traits) {
14
+ for (const trait of traits) {
15
+ if (!this.entries.delete(trait)) continue;
16
+ this.emit(trait);
17
+ }
18
+ }
19
+ get(trait) {
20
+ return this.entries.get(trait);
21
+ }
22
+ list() {
23
+ return [...this.entries.values()];
24
+ }
25
+ subscribe(trait, listener) {
26
+ let set = this.listeners.get(trait);
27
+ if (!set) {
28
+ set = /* @__PURE__ */ new Set();
29
+ this.listeners.set(trait, set);
30
+ }
31
+ set.add(listener);
32
+ return () => {
33
+ set?.delete(listener);
34
+ if (set && set.size === 0) this.listeners.delete(trait);
35
+ };
36
+ }
37
+ /** Any trait's awaiting change (an empty slot does not know which trait will fill it). */
38
+ subscribeAll(listener) {
39
+ this.anyListeners.add(listener);
40
+ return () => {
41
+ this.anyListeners.delete(listener);
42
+ };
43
+ }
44
+ /** Bumped on every change — a stable snapshot key for `useSyncExternalStore`. */
45
+ getVersion() {
46
+ return this.version;
47
+ }
48
+ emit(trait) {
49
+ this.version += 1;
50
+ this.listeners.get(trait)?.forEach((listener) => listener());
51
+ this.anyListeners.forEach((listener) => listener());
52
+ }
53
+ };
54
+
55
+ export { AwaitingRegistry };
@@ -0,0 +1,48 @@
1
+ // src/credentials/scope.ts
2
+ function credentialModeFromEnv(env) {
3
+ return env.ALMADAR_INTEGRATIONS_MODE === "mock" || env.USE_MOCK_DATA === "true" ? "mock" : "real";
4
+ }
5
+ var installedStore = null;
6
+ var modeControl = null;
7
+ var tenantScope;
8
+ function installCredentialScopeStorage(storage) {
9
+ tenantScope = storage;
10
+ }
11
+ function runWithCredentialScope(store, fn) {
12
+ if (tenantScope === void 0) throw new Error("runWithCredentialScope needs an installed CredentialScopeStorage (a Node host installs it through @almadar/shell-node)");
13
+ return tenantScope.run(store, fn);
14
+ }
15
+ function installModeControl(control) {
16
+ modeControl = control;
17
+ }
18
+ function getModeControl() {
19
+ return modeControl;
20
+ }
21
+ function installCredentialStore(store) {
22
+ installedStore = store;
23
+ }
24
+ function getInstalledCredentialStore() {
25
+ return installedStore;
26
+ }
27
+ function getEffectiveCredentialStore() {
28
+ return tenantScope?.getStore() ?? installedStore;
29
+ }
30
+ function uninstallCredentialStore() {
31
+ installedStore = null;
32
+ }
33
+ var CREDENTIALS_SOURCE_ENV = "ALMADAR_CREDENTIALS_SOURCE";
34
+ var HOST_SETTING_VARS = /* @__PURE__ */ new Set(["ALMADAR_CREDENTIAL_ADMIN_ROLES", CREDENTIALS_SOURCE_ENV, "ALMADAR_INTEGRATIONS_MODE", "USE_MOCK_DATA"]);
35
+ function credentialsFromStoreOnly(env) {
36
+ return env[CREDENTIALS_SOURCE_ENV] === "store";
37
+ }
38
+ function resolveCredentialRef(ref, env = typeof process === "undefined" ? {} : process.env, principal) {
39
+ const tenant = tenantScope?.getStore();
40
+ if (tenant) return tenant.resolve(ref, principal);
41
+ if (principal !== void 0) return installedStore?.resolve(ref, principal);
42
+ const stored = installedStore?.resolve(ref);
43
+ if (stored !== void 0) return stored;
44
+ if (credentialsFromStoreOnly(env) && !HOST_SETTING_VARS.has(ref)) return void 0;
45
+ return env[ref];
46
+ }
47
+
48
+ export { CREDENTIALS_SOURCE_ENV, credentialModeFromEnv, credentialsFromStoreOnly, getEffectiveCredentialStore, getInstalledCredentialStore, getModeControl, installCredentialScopeStorage, installCredentialStore, installModeControl, resolveCredentialRef, runWithCredentialScope, uninstallCredentialStore };