@nylorun/runtime 0.14.0-beta → 0.15.1-beta

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 (198) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/README.md +82 -40
  3. package/dist/adapters/execution/restate.js +2 -2
  4. package/dist/adapters/replication/pgoutput.d.ts +5 -0
  5. package/dist/adapters/replication/pgoutput.js +4 -3
  6. package/dist/admin-openapi.json +185 -686
  7. package/dist/api/components.d.ts +308 -68
  8. package/dist/api/components.js +4 -5
  9. package/dist/api/gate/keys.d.ts +14 -0
  10. package/dist/api/gate/keys.js +59 -0
  11. package/dist/api/gate/routes.d.ts +34 -0
  12. package/dist/api/gate/routes.js +235 -0
  13. package/dist/api/http/define.js +0 -8
  14. package/dist/api/http/routes/access.js +25 -18
  15. package/dist/api/http/routes/tenant.d.ts +2 -1
  16. package/dist/api/http/routes/tenant.js +77 -5
  17. package/dist/api/http/routes/vaults.js +2 -2
  18. package/dist/api/openapi.js +6 -7
  19. package/dist/gates/contract.d.ts +51 -0
  20. package/dist/gates/contract.js +37 -0
  21. package/dist/gates/handler.d.ts +21 -0
  22. package/dist/gates/handler.js +55 -0
  23. package/dist/gates/http-client.d.ts +12 -0
  24. package/dist/gates/http-client.js +160 -0
  25. package/dist/gates/in-process.d.ts +32 -0
  26. package/dist/gates/in-process.js +42 -0
  27. package/dist/gates/inflight.d.ts +39 -0
  28. package/dist/gates/inflight.js +70 -0
  29. package/dist/gates/mcp-handler.d.ts +35 -0
  30. package/dist/gates/mcp-handler.js +142 -0
  31. package/dist/gates/meter.d.ts +24 -0
  32. package/dist/gates/meter.js +165 -0
  33. package/dist/gates/model-gate.d.ts +45 -0
  34. package/dist/gates/model-gate.js +1 -0
  35. package/dist/gates/tenant-vaults.d.ts +64 -0
  36. package/dist/gates/tenant-vaults.js +94 -0
  37. package/dist/gates/tool-calls.d.ts +51 -0
  38. package/dist/gates/tool-calls.js +64 -0
  39. package/dist/gates/tool-client.d.ts +12 -0
  40. package/dist/gates/tool-client.js +201 -0
  41. package/dist/gates/tool-contract.d.ts +116 -0
  42. package/dist/gates/tool-contract.js +64 -0
  43. package/dist/gates/tool-gate.d.ts +45 -0
  44. package/dist/gates/tool-gate.js +10 -0
  45. package/dist/host/admin-api.d.ts +7 -6
  46. package/dist/host/admin-api.js +9 -113
  47. package/dist/host/app.d.ts +1 -1
  48. package/dist/host/app.js +20 -24
  49. package/dist/host/config-for.d.ts +1 -1
  50. package/dist/host/config-for.js +2 -2
  51. package/dist/host/create-host.d.ts +5 -5
  52. package/dist/host/create-host.js +5 -15
  53. package/dist/host/execution.d.ts +20 -18
  54. package/dist/host/execution.js +2 -2
  55. package/dist/host/gates.d.ts +58 -0
  56. package/dist/host/gates.js +127 -0
  57. package/dist/host/http.d.ts +6 -1
  58. package/dist/host/http.js +17 -0
  59. package/dist/host/index.d.ts +1 -1
  60. package/dist/host/main.js +151 -71
  61. package/dist/host/stack-config.d.ts +71 -19
  62. package/dist/host/stack-config.js +192 -18
  63. package/dist/infra/database.d.ts +2 -2
  64. package/dist/infra/execution.d.ts +5 -5
  65. package/dist/infra/execution.js +8 -6
  66. package/dist/infra/index.d.ts +1 -1
  67. package/dist/infra/workers.d.ts +17 -17
  68. package/dist/infra/workers.js +9 -9
  69. package/dist/keys/client.d.ts +9 -0
  70. package/dist/keys/client.js +70 -0
  71. package/dist/keys/contract.d.ts +24 -0
  72. package/dist/keys/contract.js +12 -0
  73. package/dist/keys/keys.d.ts +41 -0
  74. package/dist/keys/keys.js +50 -0
  75. package/dist/mcp/connect.d.ts +43 -3
  76. package/dist/mcp/connect.js +12 -5
  77. package/dist/mcp/pool.d.ts +21 -3
  78. package/dist/mcp/pool.js +15 -2
  79. package/dist/openapi.json +888 -695
  80. package/dist/record/append.d.ts +51 -0
  81. package/dist/record/append.js +20 -0
  82. package/dist/{store/event.js → record/envelope.js} +1 -1
  83. package/dist/record/index.d.ts +11 -0
  84. package/dist/record/index.js +11 -0
  85. package/dist/store/postgres/connect.d.ts +15 -12
  86. package/dist/store/postgres/connect.js +24 -12
  87. package/dist/store/postgres/db.d.ts +16 -0
  88. package/dist/store/postgres/db.js +28 -0
  89. package/dist/store/postgres/drizzle/0000_foundation.sql +22 -0
  90. package/dist/store/postgres/drizzle/0001_baseline.sql +255 -0
  91. package/dist/store/postgres/drizzle/0002_stream_relay.sql +4 -0
  92. package/dist/store/postgres/drizzle/0003_tool_crossings.sql +9 -0
  93. package/dist/store/postgres/drizzle/meta/_journal.json +34 -0
  94. package/dist/store/postgres/migrate.d.ts +68 -0
  95. package/dist/store/postgres/migrate.js +183 -0
  96. package/dist/store/postgres/record-writer.d.ts +4 -0
  97. package/dist/store/postgres/record-writer.js +34 -0
  98. package/dist/store/postgres/record.d.ts +2 -8
  99. package/dist/store/postgres/record.js +50 -54
  100. package/dist/store/postgres/schema.d.ts +3096 -0
  101. package/dist/store/postgres/schema.js +363 -0
  102. package/dist/store/postgres/store.d.ts +2 -4
  103. package/dist/store/postgres/store.js +481 -567
  104. package/dist/store/postgres/tenant.d.ts +56 -0
  105. package/dist/store/postgres/tenant.js +154 -0
  106. package/dist/store/types.d.ts +43 -87
  107. package/dist/store/types.js +0 -1
  108. package/dist/streams/control.d.ts +1 -1
  109. package/dist/streams/control.js +6 -2
  110. package/dist/streams/relay/types.d.ts +2 -1
  111. package/dist/streams/relay/types.js +2 -1
  112. package/dist/streams/types.d.ts +5 -0
  113. package/dist/tenant/advance.d.ts +7 -2
  114. package/dist/tenant/advance.js +95 -16
  115. package/dist/tenant/cause.d.ts +25 -0
  116. package/dist/tenant/cause.js +45 -0
  117. package/dist/tenant/commands.js +4 -2
  118. package/dist/tenant/context.d.ts +26 -3
  119. package/dist/tenant/delivery-token.js +17 -18
  120. package/dist/tenant/delivery.d.ts +1 -1
  121. package/dist/tenant/delivery.js +18 -13
  122. package/dist/tenant/effects.d.ts +15 -1
  123. package/dist/tenant/effects.js +93 -17
  124. package/dist/tenant/ephemeral.d.ts +16 -7
  125. package/dist/tenant/ephemeral.js +45 -111
  126. package/dist/tenant/history.d.ts +64 -0
  127. package/dist/tenant/history.js +120 -0
  128. package/dist/tenant/model-setting.d.ts +2 -2
  129. package/dist/tenant/module.d.ts +14 -28
  130. package/dist/tenant/module.js +131 -271
  131. package/dist/tenant/paths.d.ts +11 -16
  132. package/dist/tenant/paths.js +24 -68
  133. package/dist/tenant/pool.d.ts +1 -1
  134. package/dist/tenant/pool.js +1 -1
  135. package/dist/tenant/principals.d.ts +23 -11
  136. package/dist/tenant/principals.js +61 -49
  137. package/dist/tenant/runtime.d.ts +19 -1
  138. package/dist/tenant/runtime.js +40 -26
  139. package/dist/tenant/scheduler.d.ts +4 -2
  140. package/dist/tenant/scheduler.js +4 -4
  141. package/dist/tenant/session-streams.d.ts +2 -0
  142. package/dist/tenant/session-streams.js +8 -3
  143. package/dist/tenant/status.d.ts +3 -0
  144. package/dist/tenant/status.js +1 -1
  145. package/dist/tenant/store-pg.d.ts +9 -6
  146. package/dist/tenant/store-pg.js +60 -125
  147. package/dist/tenant/streams.d.ts +9 -25
  148. package/dist/tenant/streams.js +7 -22
  149. package/dist/tenant/tokens.js +16 -14
  150. package/dist/tenant/types.d.ts +41 -92
  151. package/dist/tenant/types.js +4 -15
  152. package/dist/tenant/worker.js +4 -4
  153. package/dist/vault/host-model.d.ts +31 -0
  154. package/dist/vault/host-model.js +57 -0
  155. package/dist/vault/service.d.ts +26 -15
  156. package/dist/vault/service.js +70 -109
  157. package/dist/version.d.ts +1 -1
  158. package/dist/version.js +1 -1
  159. package/package.json +9 -5
  160. package/dist/store/memory.d.ts +0 -69
  161. package/dist/store/memory.js +0 -810
  162. package/dist/store/postgres/migrations/001_initial.d.ts +0 -27
  163. package/dist/store/postgres/migrations/001_initial.js +0 -196
  164. package/dist/store/postgres/migrations/002_session_owner.d.ts +0 -6
  165. package/dist/store/postgres/migrations/002_session_owner.js +0 -13
  166. package/dist/store/postgres/migrations/003_subject_tokens.d.ts +0 -8
  167. package/dist/store/postgres/migrations/003_subject_tokens.js +0 -42
  168. package/dist/store/postgres/migrations/004_publishable_keys.d.ts +0 -6
  169. package/dist/store/postgres/migrations/004_publishable_keys.js +0 -18
  170. package/dist/store/postgres/migrations/005_action_endpoints.d.ts +0 -6
  171. package/dist/store/postgres/migrations/005_action_endpoints.js +0 -32
  172. package/dist/store/postgres/migrations/006_drop_executors.d.ts +0 -11
  173. package/dist/store/postgres/migrations/006_drop_executors.js +0 -32
  174. package/dist/store/postgres/migrations/007_event_time.d.ts +0 -7
  175. package/dist/store/postgres/migrations/007_event_time.js +0 -14
  176. package/dist/store/postgres/migrations/008_durable_streams.d.ts +0 -13
  177. package/dist/store/postgres/migrations/008_durable_streams.js +0 -35
  178. package/dist/store/postgres/migrations/index.d.ts +0 -53
  179. package/dist/store/postgres/migrations/index.js +0 -96
  180. package/dist/store/postgres/migrations/shared/index.d.ts +0 -31
  181. package/dist/store/postgres/migrations/shared/index.js +0 -92
  182. package/dist/store/postgres/names.d.ts +0 -11
  183. package/dist/store/postgres/names.js +0 -42
  184. package/dist/store/postgres/tenants.d.ts +0 -90
  185. package/dist/store/postgres/tenants.js +0 -228
  186. package/dist/streams/relay/memory.d.ts +0 -69
  187. package/dist/streams/relay/memory.js +0 -176
  188. package/dist/tenant/envelope.d.ts +0 -7
  189. package/dist/tenant/envelope.js +0 -22
  190. package/dist/tenant/quarantine-error.d.ts +0 -6
  191. package/dist/tenant/quarantine-error.js +0 -9
  192. package/dist/tenant/quarantine.d.ts +0 -27
  193. package/dist/tenant/quarantine.js +0 -72
  194. package/dist/tenant/store-memory.d.ts +0 -16
  195. package/dist/tenant/store-memory.js +0 -111
  196. /package/dist/{store → record}/cursor.d.ts +0 -0
  197. /package/dist/{store → record}/cursor.js +0 -0
  198. /package/dist/{store/event.d.ts → record/envelope.d.ts} +0 -0
@@ -1,176 +0,0 @@
1
- export class MemoryRecord {
2
- /** `tenantId\0sessionId` → rows by seq. */
3
- rows = new Map();
4
- generations = new Map();
5
- /** Committed transactions since the slot's confirmed position. */
6
- retained = [];
7
- lsn = 0;
8
- slot;
9
- reader;
10
- /** Commits rows as one transaction (the record is written by the Session Store). */
11
- commit(rows) {
12
- if (rows.length === 0)
13
- return;
14
- for (const row of rows) {
15
- const key = keyOf(row.tenantId, row.sessionId);
16
- let list = this.rows.get(key);
17
- if (!list)
18
- this.rows.set(key, (list = []));
19
- list[row.seq] = structuredClone(row);
20
- if (!this.generations.has(row.tenantId))
21
- this.generations.set(row.tenantId, row.generation);
22
- }
23
- this.lsn += 1;
24
- const tx = { endLsn: lsnText(this.lsn), rows: rows.map((row) => structuredClone(row)) };
25
- // A slot keeps what was committed after it was created.
26
- if (this.slot)
27
- this.retained.push({ lsn: this.lsn, tx });
28
- this.reader?.deliver(tx);
29
- }
30
- /** Sets a Tenant's basin generation (a reset moves it on). */
31
- setGeneration(tenantId, generation) {
32
- this.generations.set(tenantId, generation);
33
- }
34
- /** Deletes a Tenant's rows, or one session's, as a reset or deletion does. */
35
- deleteRows(tenantId, sessionId) {
36
- for (const key of [...this.rows.keys()])
37
- if (sessionId === undefined ? key.startsWith(`${tenantId}\u0000`) : key === keyOf(tenantId, sessionId))
38
- this.rows.delete(key);
39
- }
40
- /** Forgets a Tenant (deletion). */
41
- deleteTenant(tenantId) {
42
- this.deleteRows(tenantId);
43
- this.generations.delete(tenantId);
44
- }
45
- /** Drops the slot, as `max_slot_wal_keep_size` or an operator would. */
46
- dropSlot() {
47
- this.slot = undefined;
48
- this.retained = [];
49
- this.reader?.lost(new Error("replication slot dropped"));
50
- }
51
- /** Ends the reader's connection but keeps the slot, as a network failure would. */
52
- disconnect() {
53
- this.reader?.lost(new Error("replication connection lost"));
54
- }
55
- /** A change source over this record; one at a time holds the slot. */
56
- source() {
57
- return new MemoryChangeSource(this);
58
- }
59
- async readRange(tenantId, sessionId, from, to) {
60
- const list = this.rows.get(keyOf(tenantId, sessionId)) ?? [];
61
- return list.slice(from, to).filter(Boolean).map((row) => structuredClone(row));
62
- }
63
- async heads(after, limit) {
64
- const heads = [];
65
- for (const [key, list] of [...this.rows].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))) {
66
- const [tenantId, sessionId] = key.split("\u0000");
67
- if (after && key <= keyOf(after.tenantId, after.sessionId))
68
- continue;
69
- const last = list[list.length - 1];
70
- if (!last)
71
- continue;
72
- heads.push({ tenantId, sessionId, generation: last.generation, head: list.length });
73
- if (heads.length >= limit)
74
- break;
75
- }
76
- return heads;
77
- }
78
- async generation(tenantId) {
79
- return this.generations.get(tenantId);
80
- }
81
- /** @internal */
82
- attach(reader) {
83
- if (this.reader && this.reader !== reader)
84
- return undefined;
85
- this.reader = reader;
86
- if (!this.slot)
87
- this.slot = { confirmed: this.lsn, reconcilePending: true };
88
- return {
89
- fresh: this.slot.reconcilePending,
90
- replay: this.retained.map((entry) => entry.tx),
91
- };
92
- }
93
- /** @internal */
94
- detach(reader) {
95
- if (this.reader === reader)
96
- this.reader = undefined;
97
- }
98
- /** @internal */
99
- reconciled() {
100
- if (this.slot)
101
- this.slot.reconcilePending = false;
102
- }
103
- /** @internal */
104
- confirm(lsn) {
105
- const position = Number(lsn.split("/")[1] ?? lsn);
106
- if (!this.slot || position <= this.slot.confirmed)
107
- return;
108
- this.slot.confirmed = position;
109
- this.retained = this.retained.filter((entry) => entry.lsn > position);
110
- }
111
- /** The slot's confirmed position (tests). */
112
- confirmed() {
113
- return this.slot?.confirmed;
114
- }
115
- }
116
- class MemoryChangeSource {
117
- record;
118
- handlers;
119
- timer;
120
- active = false;
121
- stopped = false;
122
- constructor(record) {
123
- this.record = record;
124
- }
125
- start(handlers) {
126
- this.handlers = handlers;
127
- this.tryAttach();
128
- }
129
- acknowledge(lsn) {
130
- if (this.active)
131
- this.record.confirm(lsn);
132
- }
133
- async reconciled() {
134
- this.record.reconciled();
135
- }
136
- async stop() {
137
- this.stopped = true;
138
- clearTimeout(this.timer);
139
- this.record.detach(this);
140
- this.active = false;
141
- }
142
- /** @internal */
143
- deliver(tx) {
144
- if (this.active)
145
- this.handlers?.onTx(tx);
146
- }
147
- /** @internal */
148
- lost(error) {
149
- if (!this.active)
150
- return;
151
- this.active = false;
152
- this.record.detach(this);
153
- this.handlers?.onInactive(error);
154
- this.retry();
155
- }
156
- tryAttach() {
157
- if (this.stopped)
158
- return;
159
- const attached = this.record.attach(this);
160
- if (!attached)
161
- return this.retry();
162
- this.active = true;
163
- this.handlers.onActive({ fresh: attached.fresh });
164
- for (const tx of attached.replay)
165
- this.handlers.onTx(tx);
166
- }
167
- retry() {
168
- if (this.stopped)
169
- return;
170
- this.timer = setTimeout(() => this.tryAttach(), 20);
171
- this.timer.unref?.();
172
- }
173
- }
174
- const keyOf = (tenantId, sessionId) => `${tenantId}\u0000${sessionId}`;
175
- /** A Postgres-looking LSN, so positions read the same in logs. */
176
- const lsnText = (n) => `0/${n}`;
@@ -1,7 +0,0 @@
1
- import { type TenantEnvelope } from "@nylorun/core/contracts";
2
- export declare function parseEnvelope(raw: unknown, expectedId?: string): TenantEnvelope;
3
- export declare function envelopeNow(input: {
4
- id: string;
5
- name: string;
6
- schemaVersion: number;
7
- }, now?: Date): TenantEnvelope;
@@ -1,22 +0,0 @@
1
- import { TenantEnvelopeSchema, } from "@nylorun/core/contracts";
2
- import { quarantine } from "./quarantine.js";
3
- export function parseEnvelope(raw, expectedId) {
4
- const parsed = TenantEnvelopeSchema.safeParse(raw);
5
- if (!parsed.success) {
6
- throw quarantine("envelope-invalid", "the Tenant envelope is missing or invalid", expectedId ? { tenantId: expectedId } : {});
7
- }
8
- if (expectedId !== undefined && parsed.data.id !== expectedId) {
9
- throw quarantine("envelope-invalid", `the Tenant envelope id ${parsed.data.id} does not match ${expectedId}`, { tenantId: expectedId });
10
- }
11
- return parsed.data;
12
- }
13
- export function envelopeNow(input, now = new Date()) {
14
- const iso = now.toISOString();
15
- return {
16
- id: input.id,
17
- name: input.name,
18
- createdAt: iso,
19
- updatedAt: iso,
20
- schemaVersion: input.schemaVersion,
21
- };
22
- }
@@ -1,6 +0,0 @@
1
- import type { Quarantine } from "./types.js";
2
- /** Typed quarantine raised while opening a Tenant Runtime (A7). */
3
- export declare class QuarantineError extends Error {
4
- readonly quarantine: Quarantine;
5
- constructor(code: Quarantine["code"], message: string, repair?: string);
6
- }
@@ -1,9 +0,0 @@
1
- /** Typed quarantine raised while opening a Tenant Runtime (A7). */
2
- export class QuarantineError extends Error {
3
- quarantine;
4
- constructor(code, message, repair = "nylo tenant status") {
5
- super(message);
6
- this.name = "QuarantineError";
7
- this.quarantine = { code, message, repair };
8
- }
9
- }
@@ -1,27 +0,0 @@
1
- import type { Quarantine } from "./types.js";
2
- /** Typed quarantine failure thrown by stores and migration. */
3
- export declare class QuarantineError extends Error implements Quarantine {
4
- readonly code: Quarantine["code"];
5
- readonly repair: string;
6
- constructor(fields: Quarantine);
7
- toQuarantine(): Quarantine;
8
- }
9
- export declare function isQuarantineError(error: unknown): error is QuarantineError;
10
- export declare function asQuarantine(error: unknown): Quarantine | undefined;
11
- /** CLI-facing repair strings for every quarantine code (B9). */
12
- export declare function repairFor(code: Quarantine["code"], detail?: {
13
- tenantId?: string;
14
- }): string;
15
- export declare function quarantine(code: Quarantine["code"], message: string, detail?: {
16
- tenantId?: string;
17
- }): QuarantineError;
18
- /** Conflict when create bootstrap material does not match an existing Tenant (HTTP 409). */
19
- export declare class TenantConflictError extends Error {
20
- readonly statusCode: 409;
21
- constructor(message?: string);
22
- }
23
- /** Delete refused because the Tenant still has live work (HTTP 409). */
24
- export declare class TenantBusyError extends Error {
25
- readonly statusCode: 409;
26
- constructor(message?: string);
27
- }
@@ -1,72 +0,0 @@
1
- /** Typed quarantine failure thrown by stores and migration. */
2
- export class QuarantineError extends Error {
3
- code;
4
- repair;
5
- constructor(fields) {
6
- super(fields.message);
7
- this.name = "QuarantineError";
8
- this.code = fields.code;
9
- this.repair = fields.repair;
10
- }
11
- toQuarantine() {
12
- return { code: this.code, message: this.message, repair: this.repair };
13
- }
14
- }
15
- export function isQuarantineError(error) {
16
- return error instanceof QuarantineError;
17
- }
18
- export function asQuarantine(error) {
19
- if (isQuarantineError(error))
20
- return error.toQuarantine();
21
- if (error &&
22
- typeof error === "object" &&
23
- "code" in error &&
24
- "message" in error &&
25
- "repair" in error) {
26
- return error;
27
- }
28
- return undefined;
29
- }
30
- /** CLI-facing repair strings for every quarantine code (B9). */
31
- export function repairFor(code, detail = {}) {
32
- const id = detail.tenantId ? ` ${detail.tenantId}` : "";
33
- switch (code) {
34
- case "kek-missing":
35
- return `nylo tenant status${id} — restore vault-kek for this Tenant (ciphertext cannot be opened without it)`;
36
- case "corrupt":
37
- return `nylo tenant status${id} — restore the Tenant from backup`;
38
- case "schema-too-new":
39
- return `nylo tenant status${id} — the Tenant was migrated by a newer Runtime; run that version or newer`;
40
- case "migration-failed":
41
- return `nylo tenant status${id} — inspect the Runtime log for the failed migration, fix it, then restart the Runtime`;
42
- case "envelope-invalid":
43
- return `nylo tenant status${id} — the Tenant envelope is missing or invalid; restore the Tenant from backup`;
44
- case "open-timeout":
45
- return `nylo tenant status${id} — open timed out; inspect the Runtime log and Postgres, then retry`;
46
- case "open-failed":
47
- return `nylo tenant status${id} — inspect Tenant logs and repair before the Host retries open`;
48
- }
49
- }
50
- export function quarantine(code, message, detail = {}) {
51
- return new QuarantineError({
52
- code,
53
- message,
54
- repair: repairFor(code, detail),
55
- });
56
- }
57
- /** Conflict when create bootstrap material does not match an existing Tenant (HTTP 409). */
58
- export class TenantConflictError extends Error {
59
- statusCode = 409;
60
- constructor(message = "Tenant already exists with different bootstrap material") {
61
- super(message);
62
- this.name = "TenantConflictError";
63
- }
64
- }
65
- /** Delete refused because the Tenant still has live work (HTTP 409). */
66
- export class TenantBusyError extends Error {
67
- statusCode = 409;
68
- constructor(message = "Tenant has live work; pass activeWork=drain|cancel or retry later") {
69
- super(message);
70
- this.name = "TenantBusyError";
71
- }
72
- }
@@ -1,16 +0,0 @@
1
- import type { SessionStore } from "../store/types.js";
2
- import { type Logger, type OpenTenantRuntime, type TenantConfig, type TenantStore } from "./types.js";
3
- export interface MemoryTenantStoreOptions {
4
- /** When set, each Tenant's directory under this Host root lives as long as the Tenant. */
5
- hostRoot?: string;
6
- openRuntime: OpenTenantRuntime;
7
- configFor: (tenantId: string) => TenantConfig;
8
- logger?: Logger;
9
- }
10
- export type MemoryTenantStore = TenantStore & {
11
- /** Test helper: current Tenant ids. */
12
- ids(): string[];
13
- /** Test helper: a Session Store on a Tenant's data, as another process would open it. */
14
- sessionStore(id: string): SessionStore;
15
- };
16
- export declare function createMemoryTenantStore(options: MemoryTenantStoreOptions): MemoryTenantStore;
@@ -1,111 +0,0 @@
1
- /**
2
- * An in-memory Tenant store for tests: each Tenant is a `MemoryStoreData` (its Session
3
- * Store's data) and an envelope. It behaves like the Postgres store (`store-pg.ts`): the
4
- * Tenant directory under the Host root, when one is given, is created with the Tenant and
5
- * removed with it, and `open` hands the Tenant Runtime a fresh Session Store on the Tenant's
6
- * data. Not a supported profile.
7
- */
8
- import { mkdirSync, rmSync } from "node:fs";
9
- import { isTenantId } from "@nylorun/core/compatibility";
10
- import { MEMORY_SCHEMA_VERSION, MemorySessionStore, MemoryStoreData, } from "../store/memory.js";
11
- import { quarantine } from "./quarantine.js";
12
- import { parseEnvelope } from "./envelope.js";
13
- import { tenantPaths } from "./paths.js";
14
- import { bootstrapPrincipal, bootstrapPrincipalMatches } from "./principals.js";
15
- import { TenantNotFoundError, } from "./types.js";
16
- export function createMemoryTenantStore(options) {
17
- const records = new Map();
18
- function sessionStore(id, data) {
19
- return new MemorySessionStore({
20
- tenantId: id,
21
- onError: (error) => options.logger?.error("post-commit step failed", {
22
- message: error instanceof Error ? error.message : String(error),
23
- }),
24
- }, data);
25
- }
26
- function makeDirectory(id) {
27
- if (!options.hostRoot)
28
- return;
29
- const p = tenantPaths(options.hostRoot, id);
30
- mkdirSync(p.root, { recursive: true, mode: 0o700 });
31
- for (const dir of [p.home, p.tmp, p.sandboxes, p.pluginData, p.logs])
32
- mkdirSync(dir, { recursive: true });
33
- }
34
- function removeDirectory(id) {
35
- if (options.hostRoot)
36
- rmSync(tenantPaths(options.hostRoot, id).root, {
37
- recursive: true,
38
- force: true,
39
- });
40
- }
41
- async function withStore(id, data, fn) {
42
- const store = sessionStore(id, data);
43
- try {
44
- return await fn(store);
45
- }
46
- finally {
47
- await store.close();
48
- }
49
- }
50
- return {
51
- ids() {
52
- return [...records.keys()];
53
- },
54
- sessionStore(id) {
55
- const record = records.get(id);
56
- if (!record)
57
- throw new TenantNotFoundError(id);
58
- return sessionStore(id, record.data);
59
- },
60
- async enumerate() {
61
- return [...records.keys()].filter(isTenantId).sort();
62
- },
63
- async readEnvelope(id) {
64
- const record = records.get(id);
65
- if (!record)
66
- throw new TenantNotFoundError(id);
67
- return parseEnvelope(record.envelope, id);
68
- },
69
- async create(envelope, bootstrap) {
70
- const parsed = parseEnvelope({ ...envelope, schemaVersion: MEMORY_SCHEMA_VERSION }, envelope.id);
71
- if (records.has(parsed.id))
72
- return "exists";
73
- makeDirectory(parsed.id);
74
- const data = new MemoryStoreData();
75
- await withStore(parsed.id, data, (store) => store.tx((t) => bootstrapPrincipal(t, bootstrap)));
76
- records.set(parsed.id, { envelope: parsed, data });
77
- return "created";
78
- },
79
- async bootstrapMatches(id, bootstrap) {
80
- const record = records.get(id);
81
- if (!record)
82
- return false;
83
- return withStore(id, record.data, (store) => store.tx((t) => bootstrapPrincipalMatches(t, bootstrap)));
84
- },
85
- async open(id) {
86
- const record = records.get(id);
87
- if (!record)
88
- throw new TenantNotFoundError(id);
89
- if (record.envelope.schemaVersion > MEMORY_SCHEMA_VERSION)
90
- throw quarantine("schema-too-new", `Tenant schema version ${record.envelope.schemaVersion} is newer than this Runtime's ${MEMORY_SCHEMA_VERSION}`, { tenantId: id });
91
- makeDirectory(id);
92
- const store = sessionStore(id, record.data);
93
- try {
94
- return await options.openRuntime({ ...options.configFor(id), tenantId: id }, { store, envelope: record.envelope });
95
- }
96
- catch (error) {
97
- await store.close().catch(() => undefined);
98
- throw error;
99
- }
100
- },
101
- async trash(id) {
102
- records.delete(id);
103
- removeDirectory(id);
104
- },
105
- async removePartial(id) {
106
- if (records.has(id))
107
- return;
108
- removeDirectory(id);
109
- },
110
- };
111
- }
File without changes
File without changes
File without changes