@almadar/shell-runtime 0.1.0 → 1.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 (167) hide show
  1. package/dist/chunk-2WCVOMZ2.js +112 -0
  2. package/dist/chunk-57SBCUSM.js +62 -0
  3. package/dist/{chunk-Q2H75UYT.js → chunk-A2JJT4GA.js} +1 -37
  4. package/dist/chunk-BAS26HBN.js +10 -0
  5. package/dist/{chunk-REPX5QPI.js → chunk-BOVU4TCQ.js} +11 -3
  6. package/dist/chunk-BY5G4225.js +6 -0
  7. package/dist/chunk-DFDNHVDW.js +284 -0
  8. package/dist/{chunk-NATGCIVM.js → chunk-JVSREWQO.js} +378 -130
  9. package/dist/{chunk-DYOJGLIX.js → chunk-LKWYCYX5.js} +2 -5
  10. package/dist/chunk-LYFIQPXX.js +1 -0
  11. package/dist/{chunk-ODJCZ27I.js → chunk-N7HYNKK6.js} +35 -2
  12. package/dist/{chunk-4J2QEIUV.js → chunk-NTFQ36UN.js} +4 -30
  13. package/dist/chunk-NU6LVKGS.js +15 -0
  14. package/dist/{chunk-VNMG7NKY.js → chunk-OQVSN7DP.js} +21 -26
  15. package/dist/chunk-P2N6K2IV.js +20 -0
  16. package/dist/{chunk-QQRDUMB2.js → chunk-QCJWPXLH.js} +11 -11
  17. package/dist/{chunk-QELZ6TEJ.js → chunk-UD2CXYSJ.js} +39 -3
  18. package/dist/{chunk-UJHOUZAL.js → chunk-V4EMDPCO.js} +2 -5
  19. package/dist/chunk-VKQA2DJX.js +409 -0
  20. package/dist/chunk-VNGXRYFF.js +9 -0
  21. package/dist/{chunk-HNOAZWFH.js → chunk-WGCWJOT2.js} +2 -0
  22. package/dist/chunk-XZGVOBY6.js +33 -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 +10 -10
  56. package/dist/kernel/decode.js +1 -2
  57. package/dist/kernel/engine.d.ts +133 -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 +148 -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 +113 -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 +16 -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,62 +0,0 @@
1
- // src/runtime/events/identity/routing.ts
2
- function eventRouteKey(eventName, eventId) {
3
- return eventId ? `@evt:${eventId}` : eventName;
4
- }
5
- function buildSourceMatcher(src, listenerOrbital) {
6
- if (src.kind === "any") return () => true;
7
- if (src.kind === "trait") {
8
- if (src.traitId !== void 0) {
9
- const wantedTraitId = src.traitId;
10
- return (source) => !!source && source.traitId === wantedTraitId;
11
- }
12
- const wantedTrait2 = src.trait;
13
- return (source) => !!source && source.orbital === listenerOrbital && source.trait === wantedTrait2;
14
- }
15
- if (src.orbitalId !== void 0 && src.traitId !== void 0) {
16
- const wantedOrbitalId = src.orbitalId;
17
- const wantedTraitId = src.traitId;
18
- return (source) => !!source && source.orbitalId === wantedOrbitalId && source.traitId === wantedTraitId;
19
- }
20
- const wantedOrbital = src.orbital;
21
- const wantedTrait = src.trait;
22
- return (source) => !!source && source.orbital === wantedOrbital && source.trait === wantedTrait;
23
- }
24
- function parseListenSource(listener, listenerOrbital) {
25
- const explicit = listener.source;
26
- if (explicit && typeof explicit === "object") {
27
- return {
28
- bareEvent: listener.event,
29
- matcher: buildSourceMatcher(explicit, listenerOrbital)
30
- };
31
- }
32
- const key = listener.event;
33
- const parts = key.split(".");
34
- if (parts.length === 1) {
35
- return { bareEvent: key, matcher: () => true };
36
- }
37
- if (parts.length === 2) {
38
- const [sourceOrStar, eventName] = parts;
39
- if (sourceOrStar === "*") {
40
- return { bareEvent: eventName, matcher: () => true };
41
- }
42
- return {
43
- bareEvent: eventName,
44
- matcher: buildSourceMatcher(
45
- { kind: "trait", trait: sourceOrStar },
46
- listenerOrbital
47
- )
48
- };
49
- }
50
- if (parts.length >= 3) {
51
- const eventName = parts[parts.length - 1];
52
- const trait = parts[parts.length - 2];
53
- const orbital = parts.slice(0, parts.length - 2).join(".");
54
- return {
55
- bareEvent: eventName,
56
- matcher: buildSourceMatcher({ kind: "orbital", orbital, trait }, listenerOrbital)
57
- };
58
- }
59
- return { bareEvent: key, matcher: () => true };
60
- }
61
-
62
- export { buildSourceMatcher, eventRouteKey, parseListenSource };
@@ -1,35 +0,0 @@
1
- import { readSeededStores } from './chunk-HMK6YMQS.js';
2
- import { orbitalInlineEntities, personaFromIdentityRow } from '@almadar/core';
3
- import { identityEntitiesOf } from '@almadar/core/mock';
4
- import { createLogger } from '@almadar/logger';
5
-
6
- var log = createLogger("almadar:shell-web:mock-seed");
7
- function bindDeclaredCollections(store, entities) {
8
- for (const entity of entities) {
9
- if (entity.name && entity.collection && entity.browser !== true && entity.persistence !== "runtime") {
10
- store.bindCollection?.(entity.name, entity.collection);
11
- }
12
- }
13
- }
14
- async function seedMockStore(host, schema, viewer) {
15
- bindDeclaredCollections(host.persistence, schema.orbitals.flatMap(orbitalInlineEntities));
16
- const named = viewer.namedByHost ? viewer.current() : void 0;
17
- const dataset = await host.seeder.dataset(schema, { nowMs: host.nowMs, ...named !== void 0 ? { viewerId: named.id } : {} });
18
- if (viewer.namedByHost) return { viewer: named, journal: dataset.journal };
19
- const declared = host.seeder.config.defaultPersona;
20
- if (declared === "") return { viewer: void 0, journal: dataset.journal };
21
- const identity = identityEntitiesOf(schema.orbitals)[0];
22
- const row = identity?.collection !== void 0 ? (dataset.stores[identity.collection] ?? []).find((r) => r.id === declared) : void 0;
23
- const resolved = row === void 0 ? void 0 : personaFromIdentityRow(row);
24
- if (resolved === void 0) throw new Error(`std-mock-seed: defaultPersona ${declared} names no ${identity?.name ?? "identity"} row of the seeded dataset`);
25
- const restamp = await host.seeder.restamp(schema, dataset.stores, dataset.journal, resolved, true);
26
- log.debug("seeded", { identity: identity?.name, viewer: resolved.id, changes: restamp.changes.length });
27
- return { viewer: resolved, journal: restamp.journal };
28
- }
29
- async function restampMockOwner(host, schema, journal, user) {
30
- const stores = await readSeededStores(host.persistence, schema);
31
- const restamp = await host.seeder.restamp(schema, stores, journal, user, false);
32
- return restamp.journal;
33
- }
34
-
35
- export { bindDeclaredCollections, restampMockOwner, seedMockStore };
@@ -1,54 +0,0 @@
1
- import { isInlineTrait } from '@almadar/core';
2
-
3
- // src/runtime/effects/composition/event-wiring.ts
4
- function findInlineTrait(orbitals, name) {
5
- for (const orbital of orbitals) {
6
- for (const traitRef of orbital.traits) {
7
- if (isInlineTrait(traitRef) && traitRef.name === name) {
8
- return traitRef;
9
- }
10
- }
11
- }
12
- return null;
13
- }
14
- function hasEmit(emits, event) {
15
- return emits.some((e) => e.event === event);
16
- }
17
- function hasListen(listens, event, triggers) {
18
- return listens.some((l) => l.event === event && l.triggers === triggers);
19
- }
20
- function applyEventWiring(orbitals, wiring) {
21
- const cloned = JSON.parse(
22
- JSON.stringify(orbitals)
23
- );
24
- for (const entry of wiring) {
25
- const sourceTrait = findInlineTrait(cloned, entry.from);
26
- if (sourceTrait) {
27
- if (!sourceTrait.emits) {
28
- sourceTrait.emits = [];
29
- }
30
- if (!hasEmit(sourceTrait.emits, entry.event)) {
31
- sourceTrait.emits.push({
32
- event: entry.event,
33
- scope: "external"
34
- });
35
- }
36
- }
37
- const targetTrait = findInlineTrait(cloned, entry.to);
38
- if (targetTrait) {
39
- if (!targetTrait.listens) {
40
- targetTrait.listens = [];
41
- }
42
- if (!hasListen(targetTrait.listens, entry.event, entry.triggers)) {
43
- targetTrait.listens.push({
44
- event: entry.event,
45
- triggers: entry.triggers,
46
- scope: "external"
47
- });
48
- }
49
- }
50
- }
51
- return cloned;
52
- }
53
-
54
- export { applyEventWiring };
@@ -1,7 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __export = (target, all) => {
3
- for (var name in all)
4
- __defProp(target, name, { get: all[name], enumerable: true });
5
- };
6
-
7
- export { __export };
@@ -1,322 +0,0 @@
1
- import { credentialStorageKey } from '@almadar/core';
2
-
3
- // src/credentials/store.ts
4
- var CREDENTIAL_ENTITY_TYPE = "AlmadarIntegrationCredential";
5
- var CREDENTIAL_MASTER_KEY_ENV = "ALMADAR_CREDENTIAL_MASTER_KEY";
6
- var CREDENTIAL_PREVIOUS_MASTER_KEY_ENV = "ALMADAR_CREDENTIAL_MASTER_KEY_PREVIOUS";
7
- var TAG_BYTES = 16;
8
- var IV_BYTES = 12;
9
- function fromHex(hex) {
10
- const bytes = new Uint8Array(hex.length / 2);
11
- for (let i = 0; i < bytes.length; i += 1) bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
12
- return bytes;
13
- }
14
- function toHex(bytes) {
15
- return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
16
- }
17
- function utf8(text) {
18
- return new TextEncoder().encode(text);
19
- }
20
- async function sha256Hex(bytes) {
21
- return toHex(new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", bytes)));
22
- }
23
- function environment(name) {
24
- return typeof process === "undefined" ? void 0 : process.env[name];
25
- }
26
- var DERIVED_ENTITY_TYPE = "AlmadarKernelDerivedSecret";
27
- function keepsDerived(adapter) {
28
- return "getById" in adapter && typeof adapter.getById === "function";
29
- }
30
- function derivedId(identity) {
31
- return sha256Hex(utf8(credentialStorageKey(identity)));
32
- }
33
- function isNonEmptyString(v) {
34
- return typeof v === "string" && v.length > 0;
35
- }
36
- async function keyIdOf(masterKeyHex) {
37
- return (await sha256Hex(fromHex(masterKeyHex))).slice(0, 16);
38
- }
39
- var MASTER_KEY_SHAPE = /^[0-9a-fA-F]{64}$/;
40
- var CredentialStore = class {
41
- adapter;
42
- masterKeyHex;
43
- previousKeyHex;
44
- byIdentity = /* @__PURE__ */ new Map();
45
- listeners = /* @__PURE__ */ new Set();
46
- warmed = false;
47
- constructor(adapter, masterKey, previousMasterKey) {
48
- this.adapter = adapter;
49
- const key = masterKey ?? environment(CREDENTIAL_MASTER_KEY_ENV);
50
- const valid = typeof key === "string" && MASTER_KEY_SHAPE.test(key);
51
- if (!valid && environment("NODE_ENV") === "production") {
52
- throw new Error(
53
- `${CREDENTIAL_MASTER_KEY_ENV} must be a 64-character hex string (32 bytes) in production \u2014 generate one with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"`
54
- );
55
- }
56
- this.masterKeyHex = valid ? key : void 0;
57
- const prev = previousMasterKey ?? environment(CREDENTIAL_PREVIOUS_MASTER_KEY_ENV);
58
- this.previousKeyHex = typeof prev === "string" && MASTER_KEY_SHAPE.test(prev) ? prev : void 0;
59
- }
60
- /** True when a valid master key is present (writes and decryption enabled). */
61
- get enabled() {
62
- return this.masterKeyHex !== void 0;
63
- }
64
- /** Subscribe to credential changes; returns an unsubscribe function. */
65
- subscribe(listener) {
66
- this.listeners.add(listener);
67
- return () => this.listeners.delete(listener);
68
- }
69
- notify(principal) {
70
- for (const listener of this.listeners) listener(principal);
71
- }
72
- async encrypt(plaintext, identity) {
73
- if (!this.masterKeyHex) {
74
- throw new Error(`${CREDENTIAL_MASTER_KEY_ENV} is not configured \u2014 cannot store credentials`);
75
- }
76
- const iv = globalThis.crypto.getRandomValues(new Uint8Array(IV_BYTES));
77
- const key = await globalThis.crypto.subtle.importKey("raw", fromHex(this.masterKeyHex), "AES-GCM", false, ["encrypt"]);
78
- const sealed = new Uint8Array(
79
- await globalThis.crypto.subtle.encrypt(
80
- { name: "AES-GCM", iv, tagLength: TAG_BYTES * 8, ...identity?.principal !== void 0 ? { additionalData: utf8(credentialStorageKey(identity)) } : {} },
81
- key,
82
- utf8(plaintext)
83
- )
84
- );
85
- return {
86
- ciphertext: toHex(sealed.subarray(0, sealed.length - TAG_BYTES)),
87
- iv: toHex(iv),
88
- authTag: toHex(sealed.subarray(sealed.length - TAG_BYTES)),
89
- keyId: await keyIdOf(this.masterKeyHex)
90
- };
91
- }
92
- async decryptWithKey(keyHex, ciphertext, ivHex, authTagHex, identity) {
93
- const key = await globalThis.crypto.subtle.importKey("raw", fromHex(keyHex), "AES-GCM", false, ["decrypt"]);
94
- const body = fromHex(ciphertext);
95
- const tag = fromHex(authTagHex);
96
- const joined = new Uint8Array(body.length + tag.length);
97
- joined.set(body, 0);
98
- joined.set(tag, body.length);
99
- const plain = await globalThis.crypto.subtle.decrypt(
100
- { name: "AES-GCM", iv: fromHex(ivHex), tagLength: TAG_BYTES * 8, ...identity?.principal !== void 0 ? { additionalData: utf8(credentialStorageKey(identity)) } : {} },
101
- key,
102
- joined
103
- );
104
- return new TextDecoder().decode(plain);
105
- }
106
- /**
107
- * Decrypt one row, selecting the key by the row's stamped `keyId` (I-28):
108
- * current key first, then the rotation-window previous key. Legacy rows
109
- * (no `keyId`) try current then previous. Returns null when no held key
110
- * decrypts the row — the row is unreadable, never silently wrong (GCM
111
- * auth tags make a wrong-key success impossible).
112
- */
113
- async decryptRow(ciphertext, ivHex, authTagHex, rowKeyId, identity) {
114
- if (!this.masterKeyHex) {
115
- throw new Error(`${CREDENTIAL_MASTER_KEY_ENV} is not configured \u2014 cannot decrypt credentials`);
116
- }
117
- const candidates = [];
118
- if (rowKeyId === void 0 || rowKeyId === await keyIdOf(this.masterKeyHex)) {
119
- candidates.push(this.masterKeyHex);
120
- }
121
- if (this.previousKeyHex !== void 0 && (rowKeyId === void 0 || rowKeyId === await keyIdOf(this.previousKeyHex))) {
122
- candidates.push(this.previousKeyHex);
123
- }
124
- for (const key of candidates) {
125
- try {
126
- return await this.decryptWithKey(key, ciphertext, ivHex, authTagHex, identity);
127
- } catch {
128
- }
129
- }
130
- return null;
131
- }
132
- /**
133
- * Load and decrypt every stored row into memory. Returns the number of
134
- * resolvable credentials. Without a master key nothing decrypts (rows are
135
- * left in place, resolution falls through to env).
136
- */
137
- async warm() {
138
- this.byIdentity.clear();
139
- this.warmed = true;
140
- if (!this.enabled) return 0;
141
- const rows = await this.adapter.list(CREDENTIAL_ENTITY_TYPE);
142
- for (const row of rows) {
143
- const { id, service, envVar, ciphertext, iv, authTag, principal } = row;
144
- if (!isNonEmptyString(id) || !isNonEmptyString(service) || !isNonEmptyString(envVar) || typeof ciphertext !== "string" || !isNonEmptyString(iv) || !isNonEmptyString(authTag) || principal !== void 0 && !isNonEmptyString(principal)) {
145
- continue;
146
- }
147
- const value = await this.decryptRow(
148
- ciphertext,
149
- iv,
150
- authTag,
151
- isNonEmptyString(row.keyId) ? row.keyId : void 0,
152
- { name: envVar, ...principal !== void 0 ? { principal } : {} }
153
- );
154
- if (value === null) continue;
155
- this.byIdentity.set(credentialStorageKey({ name: envVar, ...principal !== void 0 ? { principal } : {} }), {
156
- id,
157
- service,
158
- envVar,
159
- value,
160
- last4: value.slice(-4),
161
- updatedAt: typeof row.updatedAt === "number" ? row.updatedAt : 0,
162
- ...isNonEmptyString(row.label) ? { label: row.label } : {},
163
- ...principal !== void 0 ? { principal } : {}
164
- });
165
- }
166
- return this.byIdentity.size;
167
- }
168
- /** Resolve one credential by env-var name (warmed values only). */
169
- resolve(envVar, principal) {
170
- return this.byIdentity.get(credentialStorageKey({ name: envVar, ...principal !== void 0 ? { principal } : {} }))?.value;
171
- }
172
- /** The non-secret account label stored in the clear beside a credential's ciphertext; undefined when its row carries none. */
173
- label(envVar, principal) {
174
- return this.byIdentity.get(credentialStorageKey({ name: envVar, ...principal !== void 0 ? { principal } : {} }))?.label;
175
- }
176
- /** All warmed values as an env-shaped map, for merging over the process env. */
177
- snapshotEnv() {
178
- const env = {};
179
- for (const entry of this.byIdentity.values()) {
180
- if (entry.principal === void 0) env[entry.envVar] = entry.value;
181
- }
182
- return env;
183
- }
184
- /** Masked entries for display — never includes plaintext. */
185
- entries(principal) {
186
- return Array.from(this.byIdentity.values()).filter((entry) => entry.principal === principal).map(({ service, envVar, last4, updatedAt, principal: owner }) => ({ service, envVar, last4, updatedAt, ...owner !== void 0 ? { principal: owner } : {} })).sort((a, b) => a.service.localeCompare(b.service) || a.envVar.localeCompare(b.envVar));
187
- }
188
- /**
189
- * Upsert one credential (encrypts, persists, re-warms the entry, notifies). `label` is a non-secret account name kept
190
- * in the clear in the row beside the ciphertext; left out, the row's label stays as it is.
191
- */
192
- async set(service, envVar, value, principal, label) {
193
- if (principal !== void 0 && !isNonEmptyString(principal)) throw new Error("credential principal must be a nonempty string");
194
- if (label !== void 0 && !isNonEmptyString(label)) throw new Error("credential label must be a nonempty string");
195
- if (!this.warmed) await this.warm();
196
- const identity = { name: envVar, ...principal !== void 0 ? { principal } : {} };
197
- const storageKey = credentialStorageKey(identity);
198
- const { ciphertext, iv, authTag, keyId } = await this.encrypt(value, identity);
199
- const updatedAt = Date.now();
200
- const existing = this.byIdentity.get(storageKey);
201
- const data = { service, envVar, ciphertext, iv, authTag, keyId, updatedAt, ...principal !== void 0 ? { principal } : {}, ...label !== void 0 ? { label } : {} };
202
- let id;
203
- if (existing) {
204
- id = existing.id;
205
- await this.adapter.update(CREDENTIAL_ENTITY_TYPE, id, data);
206
- } else {
207
- ({ id } = await this.adapter.create(CREDENTIAL_ENTITY_TYPE, data));
208
- }
209
- const kept = label ?? existing?.label;
210
- const entry = { id, service, envVar, value, last4: value.slice(-4), updatedAt, ...kept !== void 0 ? { label: kept } : {}, ...principal !== void 0 ? { principal } : {} };
211
- this.byIdentity.set(storageKey, entry);
212
- this.notify(principal);
213
- const { service: s, envVar: e, last4, updatedAt: u } = entry;
214
- return { service: s, envVar: e, last4, updatedAt: u, ...principal !== void 0 ? { principal } : {} };
215
- }
216
- /**
217
- * Re-encrypt every readable row under the CURRENT master key (I-28).
218
- * Rotation window: deploy the new key as `ALMADAR_CREDENTIAL_MASTER_KEY`
219
- * with the old one as `..._PREVIOUS`, call `rotate()`, then retire the
220
- * previous key. Rows no held key can read are counted, never dropped.
221
- */
222
- async rotate() {
223
- if (!this.masterKeyHex) {
224
- throw new Error(`${CREDENTIAL_MASTER_KEY_ENV} is not configured \u2014 cannot rotate`);
225
- }
226
- const currentKeyId = await keyIdOf(this.masterKeyHex);
227
- let rotated = 0;
228
- let alreadyCurrent = 0;
229
- let unreadable = 0;
230
- const rows = await this.adapter.list(CREDENTIAL_ENTITY_TYPE);
231
- for (const row of rows) {
232
- const { id, service, envVar, ciphertext, iv, authTag, principal } = row;
233
- if (!isNonEmptyString(id) || !isNonEmptyString(service) || !isNonEmptyString(envVar) || typeof ciphertext !== "string" || !isNonEmptyString(iv) || !isNonEmptyString(authTag) || principal !== void 0 && !isNonEmptyString(principal)) {
234
- unreadable += 1;
235
- continue;
236
- }
237
- const rowKeyId = isNonEmptyString(row.keyId) ? row.keyId : void 0;
238
- const identity = { name: envVar, ...principal !== void 0 ? { principal } : {} };
239
- const value = await this.decryptRow(ciphertext, iv, authTag, rowKeyId, identity);
240
- if (value === null) {
241
- unreadable += 1;
242
- continue;
243
- }
244
- if (rowKeyId === currentKeyId) {
245
- alreadyCurrent += 1;
246
- continue;
247
- }
248
- if (rowKeyId === void 0 && await this.decryptRow(ciphertext, iv, authTag, currentKeyId, identity) !== null) {
249
- alreadyCurrent += 1;
250
- await this.adapter.update(CREDENTIAL_ENTITY_TYPE, id, { ...row, keyId: currentKeyId });
251
- continue;
252
- }
253
- const fresh = await this.encrypt(value, identity);
254
- await this.adapter.update(CREDENTIAL_ENTITY_TYPE, id, {
255
- service,
256
- envVar,
257
- ciphertext: fresh.ciphertext,
258
- iv: fresh.iv,
259
- authTag: fresh.authTag,
260
- keyId: fresh.keyId,
261
- updatedAt: Date.now(),
262
- ...principal !== void 0 ? { principal } : {}
263
- });
264
- rotated += 1;
265
- }
266
- await this.warm();
267
- this.notify();
268
- return { rotated, alreadyCurrent, unreadable };
269
- }
270
- /** Whether one-use values are shared through the adapter: a master key and an adapter that reads a row by id. */
271
- get sharesDerived() {
272
- return this.enabled && keepsDerived(this.adapter);
273
- }
274
- /** Keep `value` encrypted in the adapter until `expiresAt` (epoch ms) so any instance on the same store can take it once. */
275
- async holdDerived(name, value, expiresAt, principal) {
276
- const adapter = this.adapter;
277
- if (!this.enabled || !keepsDerived(adapter)) throw new Error("this host cannot share one-use values through its credential store");
278
- const identity = { name, ...principal !== void 0 ? { principal } : {} };
279
- const id = await derivedId(identity);
280
- const { ciphertext, iv, authTag, keyId } = await this.encrypt(value, identity);
281
- const fields = { envVar: name, ciphertext, iv, authTag, keyId, expiresAt, ...principal !== void 0 ? { principal } : {} };
282
- if (await adapter.getById(DERIVED_ENTITY_TYPE, id) !== null) await adapter.update(DERIVED_ENTITY_TYPE, id, fields);
283
- else await adapter.create(DERIVED_ENTITY_TYPE, { id, ...fields });
284
- }
285
- /**
286
- * Remove and return a held value. Where the adapter has a conditional delete only the instance whose delete
287
- * lands gets the value; elsewhere the delete is unconditional, so two instances racing for one state can both
288
- * read it. An expired value is consumed and not returned.
289
- */
290
- async takeDerived(name, principal, now) {
291
- const adapter = this.adapter;
292
- if (!this.enabled || !keepsDerived(adapter)) return void 0;
293
- const identity = { name, ...principal !== void 0 ? { principal } : {} };
294
- const id = await derivedId(identity);
295
- const row = await adapter.getById(DERIVED_ENTITY_TYPE, id);
296
- if (row === null) return void 0;
297
- const conditional = adapter.writeCapabilities?.(DERIVED_ENTITY_TYPE)?.conditional === true;
298
- const outcome = conditional ? await adapter.writeWhere?.({ kind: "delete", entityType: DERIVED_ENTITY_TYPE, id }) : void 0;
299
- if (outcome !== void 0) {
300
- if (outcome.kind !== "deleted") return void 0;
301
- } else {
302
- await adapter.delete(DERIVED_ENTITY_TYPE, id);
303
- }
304
- const { ciphertext, iv, authTag, expiresAt } = row;
305
- if (typeof expiresAt !== "number" || expiresAt <= now) return void 0;
306
- if (typeof ciphertext !== "string" || !isNonEmptyString(iv) || !isNonEmptyString(authTag)) return void 0;
307
- return await this.decryptRow(ciphertext, iv, authTag, isNonEmptyString(row.keyId) ? row.keyId : void 0, identity) ?? void 0;
308
- }
309
- /** Delete one credential row; resolution falls back to env afterwards. */
310
- async remove(envVar, principal) {
311
- if (!this.warmed) await this.warm();
312
- const storageKey = credentialStorageKey({ name: envVar, ...principal !== void 0 ? { principal } : {} });
313
- const existing = this.byIdentity.get(storageKey);
314
- if (!existing) return false;
315
- await this.adapter.delete(CREDENTIAL_ENTITY_TYPE, existing.id);
316
- this.byIdentity.delete(storageKey);
317
- this.notify(principal);
318
- return true;
319
- }
320
- };
321
-
322
- export { CREDENTIAL_ENTITY_TYPE, CREDENTIAL_MASTER_KEY_ENV, CREDENTIAL_PREVIOUS_MASTER_KEY_ENV, CredentialStore, DERIVED_ENTITY_TYPE };
@@ -1,9 +0,0 @@
1
- import { CREDENTIAL_MASTER_KEY_ENV, CREDENTIAL_PREVIOUS_MASTER_KEY_ENV } from './chunk-NAECVIAR.js';
2
-
3
- // src/credentials/env-vars.ts
4
- var STORE_BOOTSTRAP_ENV_VARS = /* @__PURE__ */ new Set([CREDENTIAL_MASTER_KEY_ENV, CREDENTIAL_PREVIOUS_MASTER_KEY_ENV]);
5
- function allCredentialEnvVars() {
6
- return new Set(STORE_BOOTSTRAP_ENV_VARS);
7
- }
8
-
9
- export { STORE_BOOTSTRAP_ENV_VARS, allCredentialEnvVars };