@almadar/shell-runtime 0.1.0 → 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 (167) hide show
  1. package/dist/chunk-57SBCUSM.js +62 -0
  2. package/dist/chunk-625EP7W2.js +108 -0
  3. package/dist/{chunk-NATGCIVM.js → chunk-7WB6VWRK.js} +344 -129
  4. package/dist/{chunk-Q2H75UYT.js → chunk-A2JJT4GA.js} +1 -37
  5. package/dist/chunk-BAS26HBN.js +10 -0
  6. package/dist/{chunk-REPX5QPI.js → chunk-BOVU4TCQ.js} +11 -3
  7. package/dist/chunk-BY5G4225.js +6 -0
  8. package/dist/chunk-DFDNHVDW.js +284 -0
  9. package/dist/chunk-I3TKINSO.js +398 -0
  10. package/dist/{chunk-DYOJGLIX.js → chunk-LKWYCYX5.js} +2 -5
  11. package/dist/chunk-LYFIQPXX.js +1 -0
  12. package/dist/{chunk-VNMG7NKY.js → chunk-MU335ZA4.js} +20 -26
  13. package/dist/{chunk-ODJCZ27I.js → chunk-N7HYNKK6.js} +35 -2
  14. package/dist/{chunk-4J2QEIUV.js → chunk-NTFQ36UN.js} +4 -30
  15. package/dist/chunk-NU6LVKGS.js +15 -0
  16. package/dist/chunk-P2N6K2IV.js +20 -0
  17. package/dist/{chunk-QQRDUMB2.js → chunk-QCJWPXLH.js} +11 -11
  18. package/dist/{chunk-QELZ6TEJ.js → chunk-UD2CXYSJ.js} +39 -3
  19. package/dist/{chunk-UJHOUZAL.js → chunk-V4EMDPCO.js} +2 -5
  20. package/dist/chunk-VNGXRYFF.js +9 -0
  21. package/dist/{chunk-HNOAZWFH.js → chunk-WGCWJOT2.js} +2 -0
  22. package/dist/chunk-X6W44WHO.js +26 -0
  23. package/dist/chunk-ZUSM4VW4.js +197 -0
  24. package/dist/client/EventTransport.d.ts +23 -7
  25. package/dist/client/EventTransport.js +1 -2
  26. package/dist/client/awaiting.js +0 -1
  27. package/dist/client/channel-transport.d.ts +6 -6
  28. package/dist/client/channel-transport.js +0 -1
  29. package/dist/client/circuit-store.d.ts +16 -5
  30. package/dist/client/circuit-store.js +2 -3
  31. package/dist/client/client-kernel.d.ts +36 -15
  32. package/dist/client/declared-input-dispatch.d.ts +19 -2
  33. package/dist/client/declared-input-dispatch.js +1 -2
  34. package/dist/client/inbound-dispatch.d.ts +21 -2
  35. package/dist/client/inbound-dispatch.js +1 -2
  36. package/dist/client/local-fold.d.ts +15 -8
  37. package/dist/client/local-fold.js +1 -2
  38. package/dist/client/render-sigils.js +0 -1
  39. package/dist/client/state-book.d.ts +9 -16
  40. package/dist/client/state-book.js +1 -2
  41. package/dist/client/trait-mount-error.js +0 -1
  42. package/dist/credentials/env-vars.d.ts +5 -1
  43. package/dist/credentials/env-vars.js +1 -3
  44. package/dist/credentials/scope.d.ts +8 -7
  45. package/dist/credentials/scope.js +0 -1
  46. package/dist/credentials/vault.d.ts +142 -0
  47. package/dist/credentials/vault.js +1 -0
  48. package/dist/effects/in-flight-calls.js +0 -1
  49. package/dist/effects/on-device-model.js +0 -1
  50. package/dist/events/EventBus.d.ts +1 -4
  51. package/dist/events/EventBus.js +0 -1
  52. package/dist/events/emit-stamp.js +0 -1
  53. package/dist/index.d.ts +25 -42
  54. package/dist/index.js +27 -45
  55. package/dist/kernel/decode.d.ts +8 -10
  56. package/dist/kernel/decode.js +1 -2
  57. package/dist/kernel/engine.d.ts +132 -31
  58. package/dist/kernel/engine.js +7 -9
  59. package/dist/kernel/frame-applier.d.ts +8 -7
  60. package/dist/kernel/frame-applier.js +1 -2
  61. package/dist/kernel/host.d.ts +127 -16
  62. package/dist/kernel/host.js +4 -7
  63. package/dist/kernel/lane.d.ts +1 -22
  64. package/dist/kernel/lane.js +1 -2
  65. package/dist/kernel/os-fetch-port.d.ts +7 -0
  66. package/dist/kernel/os-fetch-port.js +3 -0
  67. package/dist/kernel/os-port.d.ts +30 -8
  68. package/dist/kernel/os-port.js +1 -2
  69. package/dist/kernel/program-effects.d.ts +11 -28
  70. package/dist/kernel/program-effects.js +1 -2
  71. package/dist/kernel/secret-ports.d.ts +139 -0
  72. package/dist/kernel/secret-ports.js +2 -0
  73. package/dist/kernel/wire.d.ts +50 -12
  74. package/dist/loader/fetch-loader.d.ts +26 -0
  75. package/dist/loader/fetch-loader.js +1 -0
  76. package/dist/loader/lazy-page.js +0 -1
  77. package/dist/loader/schema-loader.d.ts +1 -17
  78. package/dist/persistence/native-memory.js +0 -1
  79. package/dist/runtime/entities/declaredCollections.d.ts +6 -0
  80. package/dist/runtime/entities/declaredCollections.js +1 -0
  81. package/dist/runtime/evaluation/local-store-transport.d.ts +14 -23
  82. package/dist/runtime/evaluation/local-store-transport.js +9 -13
  83. package/dist/runtime/evaluation/residence-transport.d.ts +3 -3
  84. package/dist/runtime/evaluation/residence-transport.js +1 -2
  85. package/dist/runtime/evaluation/routed-persistence.js +0 -1
  86. package/dist/runtime/events/identity/routing.d.ts +2 -53
  87. package/dist/runtime/events/identity/routing.js +1 -2
  88. package/dist/runtime/types.d.ts +5 -205
  89. package/dist/runtime/types.js +1 -2
  90. package/dist/service/default-call-service.js +0 -1
  91. package/dist/service/service-provider.js +0 -1
  92. package/package.json +8 -5
  93. package/dist/chunk-2JU7Y4KU.js +0 -21
  94. package/dist/chunk-46RWK2B5.js +0 -28
  95. package/dist/chunk-4ZICQ5PM.js +0 -192
  96. package/dist/chunk-53IPZXDP.js +0 -76
  97. package/dist/chunk-5D63EMO5.js +0 -16
  98. package/dist/chunk-CPSQXEJ6.js +0 -55
  99. package/dist/chunk-CQP5XAX6.js +0 -173
  100. package/dist/chunk-CROS2GEX.js +0 -49
  101. package/dist/chunk-D4XJTBLB.js +0 -217
  102. package/dist/chunk-D5USD2EX.js +0 -232
  103. package/dist/chunk-EV56K4GB.js +0 -63
  104. package/dist/chunk-FQ6IZMIU.js +0 -10
  105. package/dist/chunk-HMK6YMQS.js +0 -135
  106. package/dist/chunk-IHQLE5M7.js +0 -62
  107. package/dist/chunk-JFWFDUTR.js +0 -35
  108. package/dist/chunk-MHYSHSRG.js +0 -54
  109. package/dist/chunk-MLKGABMK.js +0 -7
  110. package/dist/chunk-NAECVIAR.js +0 -322
  111. package/dist/chunk-Q7JK4KY7.js +0 -9
  112. package/dist/chunk-RBUMDYFS.js +0 -681
  113. package/dist/chunk-SAS5KKCR.js +0 -43
  114. package/dist/chunk-SWPDU752.js +0 -28
  115. package/dist/chunk-T775TUCD.js +0 -18
  116. package/dist/chunk-VMOBW7RK.js +0 -21
  117. package/dist/chunk-WNPNNYXY.js +0 -22
  118. package/dist/chunk-XB5GYODW.js +0 -28
  119. package/dist/chunk-YL2SO5TP.js +0 -334
  120. package/dist/chunk-YO4LWS3I.js +0 -606
  121. package/dist/chunk-ZAQSS47X.js +0 -157
  122. package/dist/chunk-ZCQUUJIF.js +0 -129
  123. package/dist/client/ServerBridge.d.ts +0 -126
  124. package/dist/client/ServerBridge.js +0 -3
  125. package/dist/credentials/seed.d.ts +0 -21
  126. package/dist/credentials/seed.js +0 -2
  127. package/dist/credentials/store.d.ts +0 -110
  128. package/dist/credentials/store.js +0 -2
  129. package/dist/index-hT1xCdTq.d.ts +0 -46
  130. package/dist/kernel/beacon.d.ts +0 -32
  131. package/dist/kernel/beacon.js +0 -2
  132. package/dist/kernel/http.d.ts +0 -302
  133. package/dist/kernel/http.js +0 -6
  134. package/dist/kernel/oauth-effects.d.ts +0 -25
  135. package/dist/kernel/oauth-effects.js +0 -7
  136. package/dist/kernel/oauth-port.d.ts +0 -31
  137. package/dist/kernel/oauth-port.js +0 -2
  138. package/dist/loader/http-loader.d.ts +0 -111
  139. package/dist/loader/http-loader.js +0 -2
  140. package/dist/runtime/effects/composition/compose-behaviors.d.ts +0 -58
  141. package/dist/runtime/effects/composition/compose-behaviors.js +0 -4
  142. package/dist/runtime/effects/composition/event-wiring.d.ts +0 -38
  143. package/dist/runtime/effects/composition/event-wiring.js +0 -2
  144. package/dist/runtime/effects/composition/index.d.ts +0 -5
  145. package/dist/runtime/effects/composition/index.js +0 -6
  146. package/dist/runtime/effects/composition/layout-strategy.d.ts +0 -35
  147. package/dist/runtime/effects/composition/layout-strategy.js +0 -2
  148. package/dist/runtime/effects/composition/pipe.d.ts +0 -25
  149. package/dist/runtime/effects/composition/pipe.js +0 -2
  150. package/dist/runtime/entities/mockStoreSeed.d.ts +0 -56
  151. package/dist/runtime/entities/mockStoreSeed.js +0 -3
  152. package/dist/runtime/entities/seedBrowserStore.d.ts +0 -28
  153. package/dist/runtime/entities/seedBrowserStore.js +0 -2
  154. package/dist/runtime/entities/std-mock-seed.d.ts +0 -87
  155. package/dist/runtime/entities/std-mock-seed.js +0 -2
  156. package/dist/runtime/time/TickScheduler.d.ts +0 -77
  157. package/dist/runtime/time/TickScheduler.js +0 -3
  158. package/dist/runtime/time/cron.d.ts +0 -25
  159. package/dist/runtime/time/cron.js +0 -2
  160. package/dist/runtime/time/duration.d.ts +0 -20
  161. package/dist/runtime/time/duration.js +0 -2
  162. package/dist/runtime/traits/OrbitalTraitParsing.d.ts +0 -34
  163. package/dist/runtime/traits/OrbitalTraitParsing.js +0 -2
  164. package/dist/runtime/traits/listen-targets.d.ts +0 -16
  165. package/dist/runtime/traits/listen-targets.js +0 -3
  166. package/dist/runtime/traits/trait-index.d.ts +0 -35
  167. package/dist/runtime/traits/trait-index.js +0 -3
@@ -1,5 +1,3 @@
1
- import { normalizeEventKey, LIFECYCLE_EVENTS } from '@almadar/core';
2
-
3
1
  // src/client/state-book.ts
4
2
  function findInitialState(trait) {
5
3
  const markedInitial = trait.states?.find((s) => s.isInitial)?.name;
@@ -14,17 +12,6 @@ function createInitialTraitState(trait) {
14
12
  context: {}
15
13
  };
16
14
  }
17
- function findMatchingTransitions(trait, currentState, eventKey, eventId) {
18
- return (trait.transitions ?? []).filter((t) => {
19
- const fromMatches = Array.isArray(t.from) ? t.from.includes(currentState) : t.from === currentState;
20
- if (!fromMatches) return false;
21
- if (eventId && t.eventId) return t.eventId === eventId;
22
- return t.event === eventKey;
23
- });
24
- }
25
- function findTransition(trait, currentState, eventKey, eventId) {
26
- return findMatchingTransitions(trait, currentState, eventKey, eventId)[0];
27
- }
28
15
  var SINGLETON_SCOPE = "__singleton__";
29
16
  var key = (traitName, entityId) => `${traitName}::${entityId ?? SINGLETON_SCOPE}`;
30
17
  var StateBook = class {
@@ -51,29 +38,6 @@ var StateBook = class {
51
38
  }
52
39
  return out;
53
40
  }
54
- /** Whether the trait has an arm on the event from its current state. */
55
- canHandleEvent(traitName, eventKey, entityId, eventId) {
56
- const trait = this.traits.get(traitName);
57
- const state = this.getState(traitName, entityId);
58
- if (trait === void 0 || state === void 0) return false;
59
- return findTransition(trait, state.currentState, normalizeEventKey(eventKey), eventId) !== void 0;
60
- }
61
- /**
62
- * The lifecycle event an embedded child can be repainted with: the one it handles from its current state,
63
- * provided every matching arm stays in that state and only renders.
64
- */
65
- repaintLifecycleEvent(traitName, entityId) {
66
- const trait = this.traits.get(traitName);
67
- const state = this.getState(traitName, entityId);
68
- if (trait === void 0 || state === void 0) return void 0;
69
- for (const event of LIFECYCLE_EVENTS) {
70
- const arms = findMatchingTransitions(trait, state.currentState, event);
71
- if (arms.length === 0) continue;
72
- const repaints = arms.every((t) => t.to === state.currentState && (t.effects ?? []).every((effect) => Array.isArray(effect) && effect[0] === "render-ui"));
73
- return repaints ? event : void 0;
74
- }
75
- return void 0;
76
- }
77
41
  /** Record the state the kernel reports. No-op for a trait not on the page. */
78
42
  seedState(traitName, currentState, entityId) {
79
43
  const trait = this.traits.get(traitName);
@@ -89,4 +53,4 @@ var StateBook = class {
89
53
  }
90
54
  };
91
55
 
92
- export { StateBook, createInitialTraitState, findInitialState, findMatchingTransitions, findTransition };
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 };
@@ -1,4 +1,12 @@
1
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
+ }
2
10
  function kernelOsCompletions(kernel) {
3
11
  const deferring = JSON.parse(kernel.osDeferringOps());
4
12
  const streaming = JSON.parse(kernel.osStreamOps());
@@ -13,7 +21,7 @@ function kernelOsCompletions(kernel) {
13
21
  }
14
22
  function kernelOsGrants(matchers, server) {
15
23
  return {
16
- allows: (need) => server().osAllows(JSON.stringify(need)),
24
+ allows: (need, orbital) => server().osAllows(JSON.stringify(need), orbital),
17
25
  globMatches: (glob, path) => matchers.osGlobMatches(glob, path),
18
26
  isPublicAddress: (ip) => matchers.isPublicAddress(ip),
19
27
  anchor: (anchors) => {
@@ -50,8 +58,8 @@ function anchoredForms(anchors, path) {
50
58
  function anchoredGrants(grants, anchors) {
51
59
  return {
52
60
  ...grants,
53
- allows: (need) => need.need === "fs" ? anchoredForms(anchors, need.path).some((path) => grants.allows({ ...need, path })) : grants.allows(need)
61
+ allows: (need, orbital) => need.need === "fs" ? anchoredForms(anchors, need.path).some((path) => grants.allows({ ...need, path }, orbital)) : grants.allows(need, orbital)
54
62
  };
55
63
  }
56
64
 
57
- export { anchoredGrants, expandAnchor, kernelOsCompletions, kernelOsGrants };
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 };