@spinajs/http-socket-client 0.0.0-stage → 3.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 (134) hide show
  1. package/README.md +129 -2
  2. package/lib/cjs/browser.d.ts +4 -0
  3. package/lib/cjs/browser.d.ts.map +1 -0
  4. package/lib/cjs/browser.js +21 -0
  5. package/lib/cjs/browser.js.map +1 -0
  6. package/lib/cjs/client.d.ts +94 -0
  7. package/lib/cjs/client.d.ts.map +1 -0
  8. package/lib/cjs/client.js +139 -0
  9. package/lib/cjs/client.js.map +1 -0
  10. package/lib/cjs/commands.d.ts +30 -0
  11. package/lib/cjs/commands.d.ts.map +1 -0
  12. package/lib/cjs/commands.js +132 -0
  13. package/lib/cjs/commands.js.map +1 -0
  14. package/lib/cjs/connection.d.ts +71 -0
  15. package/lib/cjs/connection.d.ts.map +1 -0
  16. package/lib/cjs/connection.js +233 -0
  17. package/lib/cjs/connection.js.map +1 -0
  18. package/lib/cjs/contract.d.ts +27 -0
  19. package/lib/cjs/contract.d.ts.map +1 -0
  20. package/lib/cjs/contract.js +3 -0
  21. package/lib/cjs/contract.js.map +1 -0
  22. package/lib/cjs/durable.d.ts +122 -0
  23. package/lib/cjs/durable.d.ts.map +1 -0
  24. package/lib/cjs/durable.js +396 -0
  25. package/lib/cjs/durable.js.map +1 -0
  26. package/lib/cjs/emitter.d.ts +22 -0
  27. package/lib/cjs/emitter.d.ts.map +1 -0
  28. package/lib/cjs/emitter.js +82 -0
  29. package/lib/cjs/emitter.js.map +1 -0
  30. package/lib/cjs/ids.d.ts +7 -0
  31. package/lib/cjs/ids.d.ts.map +1 -0
  32. package/lib/cjs/ids.js +11 -0
  33. package/lib/cjs/ids.js.map +1 -0
  34. package/lib/cjs/index.d.ts +17 -0
  35. package/lib/cjs/index.d.ts.map +1 -0
  36. package/lib/cjs/index.js +36 -0
  37. package/lib/cjs/index.js.map +1 -0
  38. package/lib/cjs/node/file-outbox.d.ts +40 -0
  39. package/lib/cjs/node/file-outbox.d.ts.map +1 -0
  40. package/lib/cjs/node/file-outbox.js +234 -0
  41. package/lib/cjs/node/file-outbox.js.map +1 -0
  42. package/lib/cjs/node/index.d.ts +4 -0
  43. package/lib/cjs/node/index.d.ts.map +1 -0
  44. package/lib/cjs/node/index.js +21 -0
  45. package/lib/cjs/node/index.js.map +1 -0
  46. package/lib/cjs/outbox-indexeddb.d.ts +38 -0
  47. package/lib/cjs/outbox-indexeddb.d.ts.map +1 -0
  48. package/lib/cjs/outbox-indexeddb.js +212 -0
  49. package/lib/cjs/outbox-indexeddb.js.map +1 -0
  50. package/lib/cjs/outbox.d.ts +53 -0
  51. package/lib/cjs/outbox.d.ts.map +1 -0
  52. package/lib/cjs/outbox.js +41 -0
  53. package/lib/cjs/outbox.js.map +1 -0
  54. package/lib/cjs/package.json +1 -0
  55. package/lib/cjs/reconnect.d.ts +21 -0
  56. package/lib/cjs/reconnect.d.ts.map +1 -0
  57. package/lib/cjs/reconnect.js +37 -0
  58. package/lib/cjs/reconnect.js.map +1 -0
  59. package/lib/cjs/streams.d.ts +91 -0
  60. package/lib/cjs/streams.d.ts.map +1 -0
  61. package/lib/cjs/streams.js +302 -0
  62. package/lib/cjs/streams.js.map +1 -0
  63. package/lib/cjs/timeout.d.ts +4 -0
  64. package/lib/cjs/timeout.d.ts.map +1 -0
  65. package/lib/cjs/timeout.js +13 -0
  66. package/lib/cjs/timeout.js.map +1 -0
  67. package/lib/mjs/browser.d.ts +4 -0
  68. package/lib/mjs/browser.d.ts.map +1 -0
  69. package/lib/mjs/browser.js +3 -0
  70. package/lib/mjs/browser.js.map +1 -0
  71. package/lib/mjs/client.d.ts +94 -0
  72. package/lib/mjs/client.d.ts.map +1 -0
  73. package/lib/mjs/client.js +135 -0
  74. package/lib/mjs/client.js.map +1 -0
  75. package/lib/mjs/commands.d.ts +30 -0
  76. package/lib/mjs/commands.d.ts.map +1 -0
  77. package/lib/mjs/commands.js +128 -0
  78. package/lib/mjs/commands.js.map +1 -0
  79. package/lib/mjs/connection.d.ts +71 -0
  80. package/lib/mjs/connection.d.ts.map +1 -0
  81. package/lib/mjs/connection.js +229 -0
  82. package/lib/mjs/connection.js.map +1 -0
  83. package/lib/mjs/contract.d.ts +27 -0
  84. package/lib/mjs/contract.d.ts.map +1 -0
  85. package/lib/mjs/contract.js +2 -0
  86. package/lib/mjs/contract.js.map +1 -0
  87. package/lib/mjs/durable.d.ts +122 -0
  88. package/lib/mjs/durable.d.ts.map +1 -0
  89. package/lib/mjs/durable.js +392 -0
  90. package/lib/mjs/durable.js.map +1 -0
  91. package/lib/mjs/emitter.d.ts +22 -0
  92. package/lib/mjs/emitter.d.ts.map +1 -0
  93. package/lib/mjs/emitter.js +77 -0
  94. package/lib/mjs/emitter.js.map +1 -0
  95. package/lib/mjs/ids.d.ts +7 -0
  96. package/lib/mjs/ids.d.ts.map +1 -0
  97. package/lib/mjs/ids.js +8 -0
  98. package/lib/mjs/ids.js.map +1 -0
  99. package/lib/mjs/index.d.ts +17 -0
  100. package/lib/mjs/index.d.ts.map +1 -0
  101. package/lib/mjs/index.js +9 -0
  102. package/lib/mjs/index.js.map +1 -0
  103. package/lib/mjs/node/file-outbox.d.ts +40 -0
  104. package/lib/mjs/node/file-outbox.d.ts.map +1 -0
  105. package/lib/mjs/node/file-outbox.js +230 -0
  106. package/lib/mjs/node/file-outbox.js.map +1 -0
  107. package/lib/mjs/node/index.d.ts +4 -0
  108. package/lib/mjs/node/index.d.ts.map +1 -0
  109. package/lib/mjs/node/index.js +3 -0
  110. package/lib/mjs/node/index.js.map +1 -0
  111. package/lib/mjs/outbox-indexeddb.d.ts +38 -0
  112. package/lib/mjs/outbox-indexeddb.d.ts.map +1 -0
  113. package/lib/mjs/outbox-indexeddb.js +208 -0
  114. package/lib/mjs/outbox-indexeddb.js.map +1 -0
  115. package/lib/mjs/outbox.d.ts +53 -0
  116. package/lib/mjs/outbox.d.ts.map +1 -0
  117. package/lib/mjs/outbox.js +37 -0
  118. package/lib/mjs/outbox.js.map +1 -0
  119. package/lib/mjs/package.json +1 -0
  120. package/lib/mjs/reconnect.d.ts +21 -0
  121. package/lib/mjs/reconnect.d.ts.map +1 -0
  122. package/lib/mjs/reconnect.js +33 -0
  123. package/lib/mjs/reconnect.js.map +1 -0
  124. package/lib/mjs/streams.d.ts +91 -0
  125. package/lib/mjs/streams.d.ts.map +1 -0
  126. package/lib/mjs/streams.js +297 -0
  127. package/lib/mjs/streams.js.map +1 -0
  128. package/lib/mjs/timeout.d.ts +4 -0
  129. package/lib/mjs/timeout.d.ts.map +1 -0
  130. package/lib/mjs/timeout.js +9 -0
  131. package/lib/mjs/timeout.js.map +1 -0
  132. package/lib/tsconfig.cjs.tsbuildinfo +1 -0
  133. package/lib/tsconfig.mjs.tsbuildinfo +1 -0
  134. package/package.json +67 -4
@@ -0,0 +1,40 @@
1
+ import type { FailedOutboxEntry, OutboxEntry, OutboxInput, OutboxStore } from '../outbox.js';
2
+ export interface FileOutboxOptions {
3
+ /** Test seam: the rename that publishes the temp file. */
4
+ rename?: (from: string, to: string) => Promise<void>;
5
+ /** Base of the backoff between rename retries (grows linearly, capped at 5x). Default 10. */
6
+ retryDelayMs?: number;
7
+ }
8
+ /**
9
+ * The outbox of a Node service, in one JSON file. Every write goes through a temp file, fsync and rename,
10
+ * so a crash leaves the old queue or the new one, never a torn one. Changes that queue up while a write
11
+ * is in flight are committed together by the next one. Payloads must survive a JSON round trip unchanged:
12
+ * `add` refuses binary, Dates, Maps, class instances and non-finite numbers. One process at a time per file.
13
+ *
14
+ * The whole queue is rewritten on every commit, so the cost of a write grows with the queue size: meant
15
+ * for queues of up to some thousands of entries.
16
+ */
17
+ export declare class FileOutboxStore implements OutboxStore {
18
+ readonly path: string;
19
+ private cache;
20
+ private queue;
21
+ private flushing;
22
+ private readonly renameFile;
23
+ private readonly retryDelayMs;
24
+ constructor(path: string, options?: FileOutboxOptions);
25
+ add<P>(input: OutboxInput<P>): Promise<OutboxEntry<P>>;
26
+ pending(): Promise<OutboxEntry[]>;
27
+ remove(seq: number): Promise<void>;
28
+ fail(entry: FailedOutboxEntry): Promise<void>;
29
+ failed(): Promise<FailedOutboxEntry[]>;
30
+ clearFailed(seqs?: readonly number[]): Promise<void>;
31
+ private submit;
32
+ private flush;
33
+ private load;
34
+ private write;
35
+ /** Windows and antivirus scanners briefly hold the target open, which fails the rename with EPERM/EBUSY/EACCES. */
36
+ private publish;
37
+ /** Makes the rename itself durable. Windows cannot open a directory for fsync, so it is skipped there. */
38
+ private syncDirectory;
39
+ }
40
+ //# sourceMappingURL=file-outbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-outbox.d.ts","sourceRoot":"","sources":["../../../src/node/file-outbox.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAU7F,MAAM,WAAW,iBAAiB;IAChC,0DAA0D;IAC1D,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,6FAA6F;IAC7F,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA0DD;;;;;;;;GAQG;AACH,qBAAa,eAAgB,YAAW,WAAW;aAQ/B,IAAI,EAAE,MAAM;IAP9B,OAAO,CAAC,KAAK,CAA4B;IACzC,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA8C;IACzE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IAEtC,YACkB,IAAI,EAAE,MAAM,EAC5B,OAAO,GAAE,iBAAsB,EAIhC;IAEM,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAkB5D;IAEM,OAAO,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAEvC;IAEM,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIxC;IAEM,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAWnD;IAEM,MAAM,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAE5C;IAEM,WAAW,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAI1D;IAED,OAAO,CAAC,MAAM;YAUA,KAAK;YAwBL,IAAI;YAqCJ,KAAK;IAuBnB,mHAAmH;YACrG,OAAO;IAarB,0GAA0G;YAC5F,aAAa;CAkB5B"}
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FileOutboxStore = void 0;
4
+ const promises_1 = require("node:fs/promises");
5
+ const node_path_1 = require("node:path");
6
+ const RENAME_RETRIES = 5;
7
+ const TRANSIENT = new Set(['EPERM', 'EBUSY', 'EACCES']);
8
+ const empty = () => ({ version: 1, seq: 0, pending: [], failed: [] });
9
+ const isEntry = (value) => typeof value === 'object' && value !== null && typeof value.seq === 'number';
10
+ /** Where JSON would change the value, eg. `payload.bytes is a Uint8Array`; null when it survives a JSON round trip. */
11
+ function jsonChange(value, path) {
12
+ if (value === null || typeof value === 'string' || typeof value === 'boolean') {
13
+ return null;
14
+ }
15
+ if (typeof value === 'number') {
16
+ return Number.isFinite(value) ? null : `${path} is ${value}`;
17
+ }
18
+ if (Array.isArray(value)) {
19
+ for (let i = 0; i < value.length; i++) {
20
+ const changed = value[i] === undefined ? `${path}[${i}] is undefined` : jsonChange(value[i], `${path}[${i}]`);
21
+ if (changed !== null) {
22
+ return changed;
23
+ }
24
+ }
25
+ return null;
26
+ }
27
+ if (typeof value === 'object') {
28
+ const proto = Object.getPrototypeOf(value);
29
+ if (proto !== Object.prototype && proto !== null) {
30
+ return `${path} is a ${proto.constructor?.name || 'class instance'}`;
31
+ }
32
+ for (const [key, item] of Object.entries(value)) {
33
+ // dropped from the file as from the wire, so the command is sent the same
34
+ if (item === undefined) {
35
+ continue;
36
+ }
37
+ const changed = jsonChange(item, `${path}.${key}`);
38
+ if (changed !== null) {
39
+ return changed;
40
+ }
41
+ }
42
+ return null;
43
+ }
44
+ // a top-level undefined payload stays undefined; a function or symbol inside is dropped
45
+ return path === 'payload' && value === undefined ? null : `${path} is a ${typeof value}`;
46
+ }
47
+ /**
48
+ * The outbox of a Node service, in one JSON file. Every write goes through a temp file, fsync and rename,
49
+ * so a crash leaves the old queue or the new one, never a torn one. Changes that queue up while a write
50
+ * is in flight are committed together by the next one. Payloads must survive a JSON round trip unchanged:
51
+ * `add` refuses binary, Dates, Maps, class instances and non-finite numbers. One process at a time per file.
52
+ *
53
+ * The whole queue is rewritten on every commit, so the cost of a write grows with the queue size: meant
54
+ * for queues of up to some thousands of entries.
55
+ */
56
+ class FileOutboxStore {
57
+ constructor(path, options = {}) {
58
+ this.path = path;
59
+ this.cache = null;
60
+ this.queue = [];
61
+ this.flushing = false;
62
+ this.renameFile = options.rename ?? promises_1.rename;
63
+ this.retryDelayMs = options.retryDelayMs ?? 10;
64
+ }
65
+ add(input) {
66
+ try {
67
+ // A payload that cannot be serialized must fail its own caller, not the batch it would share a write with.
68
+ JSON.stringify(input.payload);
69
+ // The cache holds the payload itself and the file its JSON form: a payload JSON changes would be sent differently after a restart.
70
+ const changed = jsonChange(input.payload, 'payload');
71
+ if (changed !== null) {
72
+ throw new TypeError(`FileOutboxStore keeps payloads as JSON, which would change ${changed}: encode it as JSON first, eg. bytes as base64`);
73
+ }
74
+ }
75
+ catch (err) {
76
+ return Promise.reject(err);
77
+ }
78
+ return this.submit(true, (state) => {
79
+ const entry = { seq: ++state.seq, name: input.name, payload: structuredClone(input.payload), createdAt: Date.now(), ...(input.key !== undefined ? { key: input.key } : {}) };
80
+ state.pending.push(entry);
81
+ return structuredClone(entry);
82
+ });
83
+ }
84
+ pending() {
85
+ return this.submit(false, (state) => structuredClone(state.pending));
86
+ }
87
+ remove(seq) {
88
+ return this.submit(true, (state) => {
89
+ state.pending = state.pending.filter((entry) => entry.seq !== seq);
90
+ });
91
+ }
92
+ fail(entry) {
93
+ return this.submit(true, (state) => {
94
+ const index = state.pending.findIndex((pending) => pending.seq === entry.seq);
95
+ if (index < 0) {
96
+ return;
97
+ }
98
+ state.pending.splice(index, 1);
99
+ state.failed.push(structuredClone(entry));
100
+ state.failed.sort((a, b) => a.seq - b.seq);
101
+ });
102
+ }
103
+ failed() {
104
+ return this.submit(false, (state) => structuredClone(state.failed));
105
+ }
106
+ clearFailed(seqs) {
107
+ return this.submit(true, (state) => {
108
+ state.failed = seqs === undefined ? [] : state.failed.filter((entry) => !seqs.includes(entry.seq));
109
+ });
110
+ }
111
+ submit(writes, run) {
112
+ return new Promise((resolve, reject) => {
113
+ this.queue.push({ run, writes, resolve: resolve, reject });
114
+ if (!this.flushing) {
115
+ this.flushing = true;
116
+ void this.flush();
117
+ }
118
+ });
119
+ }
120
+ async flush() {
121
+ try {
122
+ while (this.queue.length > 0) {
123
+ const batch = this.queue;
124
+ this.queue = [];
125
+ try {
126
+ const state = await this.load();
127
+ const results = batch.map((job) => job.run(state));
128
+ if (batch.some((job) => job.writes)) {
129
+ await this.write(state);
130
+ }
131
+ batch.forEach((job, i) => job.resolve(results[i]));
132
+ }
133
+ catch (err) {
134
+ // Jobs mutated the cache in place: the file still holds the last committed queue, so reload from it.
135
+ this.cache = null;
136
+ batch.forEach((job) => job.reject(err));
137
+ }
138
+ }
139
+ }
140
+ finally {
141
+ this.flushing = false;
142
+ }
143
+ }
144
+ async load() {
145
+ if (this.cache) {
146
+ return this.cache;
147
+ }
148
+ let text;
149
+ try {
150
+ text = await (0, promises_1.readFile)(this.path, 'utf8');
151
+ }
152
+ catch (err) {
153
+ if (err.code === 'ENOENT') {
154
+ return (this.cache = empty());
155
+ }
156
+ throw err;
157
+ }
158
+ let state;
159
+ try {
160
+ state = JSON.parse(text);
161
+ }
162
+ catch (err) {
163
+ throw new Error(`outbox file ${this.path} is damaged: ${err.message}`);
164
+ }
165
+ if (state?.version !== 1 || typeof state.seq !== 'number' || !Array.isArray(state.pending) || !Array.isArray(state.failed)) {
166
+ throw new Error(`outbox file ${this.path} is not a version 1 outbox`);
167
+ }
168
+ const entries = [...state.pending, ...state.failed];
169
+ if (!entries.every(isEntry)) {
170
+ throw new Error(`outbox file ${this.path} holds a malformed entry`);
171
+ }
172
+ if (entries.some((entry) => entry.seq > state.seq)) {
173
+ throw new Error(`outbox file ${this.path} has a seq counter behind its entries`);
174
+ }
175
+ return (this.cache = state);
176
+ }
177
+ async write(state) {
178
+ await (0, promises_1.mkdir)((0, node_path_1.dirname)(this.path), { recursive: true });
179
+ const temp = `${this.path}.tmp`;
180
+ try {
181
+ const file = await (0, promises_1.open)(temp, 'w');
182
+ try {
183
+ await file.writeFile(JSON.stringify(state));
184
+ await file.sync();
185
+ }
186
+ finally {
187
+ await file.close();
188
+ }
189
+ await this.publish(temp);
190
+ }
191
+ catch (err) {
192
+ await (0, promises_1.unlink)(temp).catch(() => undefined);
193
+ throw err;
194
+ }
195
+ // An error here means the batch may have persisted although it rejects: at-least-once, and the idempotency key makes the retry safe.
196
+ await this.syncDirectory();
197
+ }
198
+ /** Windows and antivirus scanners briefly hold the target open, which fails the rename with EPERM/EBUSY/EACCES. */
199
+ async publish(temp) {
200
+ for (let attempt = 1;; attempt++) {
201
+ try {
202
+ return await this.renameFile(temp, this.path);
203
+ }
204
+ catch (err) {
205
+ if (attempt > RENAME_RETRIES || !TRANSIENT.has(err.code ?? '')) {
206
+ throw err;
207
+ }
208
+ await new Promise((resolve) => setTimeout(resolve, this.retryDelayMs * Math.min(attempt, 5)));
209
+ }
210
+ }
211
+ }
212
+ /** Makes the rename itself durable. Windows cannot open a directory for fsync, so it is skipped there. */
213
+ async syncDirectory() {
214
+ if (process.platform === 'win32') {
215
+ return;
216
+ }
217
+ try {
218
+ const dir = await (0, promises_1.open)((0, node_path_1.dirname)(this.path), 'r');
219
+ try {
220
+ await dir.sync();
221
+ }
222
+ finally {
223
+ await dir.close();
224
+ }
225
+ }
226
+ catch (err) {
227
+ if (!['EINVAL', 'EISDIR', 'EPERM'].includes(err.code ?? '')) {
228
+ throw err;
229
+ }
230
+ }
231
+ }
232
+ }
233
+ exports.FileOutboxStore = FileOutboxStore;
234
+ //# sourceMappingURL=file-outbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-outbox.js","sourceRoot":"","sources":["../../../src/node/file-outbox.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from '../index.js';
2
+ export { FileOutboxStore } from './file-outbox.js';
3
+ export type { FileOutboxOptions } from './file-outbox.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/node/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.FileOutboxStore = void 0;
18
+ __exportStar(require("../index.js"), exports);
19
+ var file_outbox_js_1 = require("./file-outbox.js");
20
+ Object.defineProperty(exports, "FileOutboxStore", { enumerable: true, get: function () { return file_outbox_js_1.FileOutboxStore; } });
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/node/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,8CAA4B;AAC5B,mDAAmD;AAA1C,iHAAA,eAAe,OAAA"}
@@ -0,0 +1,38 @@
1
+ import type { FailedOutboxEntry, OutboxEntry, OutboxInput, OutboxStore } from './outbox.js';
2
+ export interface IndexedDbOutboxOptions {
3
+ /** The IndexedDB to use; the global `indexedDB` when omitted ( tests pass fake-indexeddb's ). */
4
+ factory?: IDBFactory;
5
+ /** Refuse a second live store on the same name via `navigator.locks`. Default: on with the global IndexedDB, off with a custom `factory`. */
6
+ exclusive?: boolean;
7
+ }
8
+ /**
9
+ * Keeps the outbox in the IndexedDB database `spinajs-outbox:<name>`, so a reload resumes the queue
10
+ * ( spec �6 step 5 ). `seq` comes from the object store's key generator, which never reuses a value and
11
+ * is advanced in the transaction that stores the entry. Every method is one transaction and resolves on
12
+ * its `complete` event, with strict durability.
13
+ *
14
+ * One live client per name: with the global IndexedDB and where `navigator.locks` exists, a second store on the same name is refused
15
+ * while the first is open. The app must persist and reuse its `clientId` with this store, because
16
+ * queued entries carry keys derived from it.
17
+ */
18
+ export declare class IndexedDbOutboxStore implements OutboxStore {
19
+ readonly name: string;
20
+ private readonly options;
21
+ private db;
22
+ private releaseLock;
23
+ private lockDone;
24
+ constructor(name: string, options?: IndexedDbOutboxOptions);
25
+ add<P>(input: OutboxInput<P>): Promise<OutboxEntry<P>>;
26
+ pending(): Promise<OutboxEntry[]>;
27
+ remove(seq: number): Promise<void>;
28
+ fail(entry: FailedOutboxEntry): Promise<void>;
29
+ failed(): Promise<FailedOutboxEntry[]>;
30
+ clearFailed(seqs?: readonly number[]): Promise<void>;
31
+ /** Closes the database connection and releases the name; the next call opens it again. */
32
+ close(): Promise<void>;
33
+ private acquireLock;
34
+ private open;
35
+ private run;
36
+ private begin;
37
+ }
38
+ //# sourceMappingURL=outbox-indexeddb.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox-indexeddb.d.ts","sourceRoot":"","sources":["../../src/outbox-indexeddb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE5F,MAAM,WAAW,sBAAsB;IACrC,iGAAiG;IACjG,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,6IAA6I;IAC7I,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAuBD;;;;;;;;;GASG;AACH,qBAAa,oBAAqB,YAAW,WAAW;aAK1B,IAAI,EAAE,MAAM;IAAE,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJlE,OAAO,CAAC,EAAE,CAAqC;IAC/C,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,QAAQ,CAAiC;IAEjD,YAA4B,IAAI,EAAE,MAAM,EAAmB,OAAO,GAAE,sBAA2B,EAAI;IAEtF,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAKlE;IAEY,OAAO,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAE7C;IAEY,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9C;IAEY,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAYzD;IAEY,MAAM,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAElD;IAEY,WAAW,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAahE;IAED,0FAA0F;IAC7E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAWlC;YAEa,WAAW;IA0BzB,OAAO,CAAC,IAAI;YAyDE,GAAG;IA2CjB,OAAO,CAAC,KAAK;CAGd"}
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IndexedDbOutboxStore = void 0;
4
+ const PENDING = 'pending';
5
+ const FAILED = 'failed';
6
+ // A bump needs an `onblocked` strategy for connections of other tabs that ignore `versionchange`.
7
+ const VERSION = 1;
8
+ function explain(err, name) {
9
+ const kind = err?.name;
10
+ switch (kind) {
11
+ case 'QuotaExceededError':
12
+ return new Error('outbox is full (QuotaExceededError)', { cause: err });
13
+ case 'VersionError':
14
+ return new Error(`outbox database "${name}" was written by a newer version of this code`, { cause: err });
15
+ case 'NotFoundError':
16
+ return new Error(`outbox database "${name}" has no expected object store`, { cause: err });
17
+ default:
18
+ return err;
19
+ }
20
+ }
21
+ /**
22
+ * Keeps the outbox in the IndexedDB database `spinajs-outbox:<name>`, so a reload resumes the queue
23
+ * ( spec �6 step 5 ). `seq` comes from the object store's key generator, which never reuses a value and
24
+ * is advanced in the transaction that stores the entry. Every method is one transaction and resolves on
25
+ * its `complete` event, with strict durability.
26
+ *
27
+ * One live client per name: with the global IndexedDB and where `navigator.locks` exists, a second store on the same name is refused
28
+ * while the first is open. The app must persist and reuse its `clientId` with this store, because
29
+ * queued entries carry keys derived from it.
30
+ */
31
+ class IndexedDbOutboxStore {
32
+ constructor(name, options = {}) {
33
+ this.name = name;
34
+ this.options = options;
35
+ this.db = null;
36
+ this.releaseLock = null;
37
+ this.lockDone = null;
38
+ }
39
+ async add(input) {
40
+ const record = { name: input.name, payload: input.payload, createdAt: Date.now(), ...(input.key !== undefined ? { key: input.key } : {}) };
41
+ const seq = await this.run([PENDING], 'readwrite', (tx) => tx.objectStore(PENDING).add(record));
42
+ // structuredClone: the caller must not share the payload with what was stored
43
+ return { ...structuredClone(record), seq: seq };
44
+ }
45
+ async pending() {
46
+ return (await this.run([PENDING], 'readonly', (tx) => tx.objectStore(PENDING).getAll()));
47
+ }
48
+ async remove(seq) {
49
+ await this.run([PENDING], 'readwrite', (tx) => tx.objectStore(PENDING).delete(seq));
50
+ }
51
+ async fail(entry) {
52
+ await this.run([PENDING, FAILED], 'readwrite', (tx) => {
53
+ const pending = tx.objectStore(PENDING);
54
+ const found = pending.getKey(entry.seq);
55
+ found.onsuccess = () => {
56
+ if (found.result !== undefined) {
57
+ pending.delete(entry.seq);
58
+ tx.objectStore(FAILED).put(entry);
59
+ }
60
+ };
61
+ return found;
62
+ });
63
+ }
64
+ async failed() {
65
+ return (await this.run([FAILED], 'readonly', (tx) => tx.objectStore(FAILED).getAll()));
66
+ }
67
+ async clearFailed(seqs) {
68
+ await this.run([FAILED], 'readwrite', (tx) => {
69
+ const failed = tx.objectStore(FAILED);
70
+ if (seqs === undefined) {
71
+ return failed.clear();
72
+ }
73
+ let last = failed.count();
74
+ for (const seq of seqs) {
75
+ last = failed.delete(seq);
76
+ }
77
+ return last;
78
+ });
79
+ }
80
+ /** Closes the database connection and releases the name; the next call opens it again. */
81
+ async close() {
82
+ const opening = this.db;
83
+ (await opening?.catch(() => null))?.close();
84
+ if (this.db === opening) {
85
+ this.db = null;
86
+ }
87
+ this.releaseLock?.();
88
+ this.releaseLock = null;
89
+ // the lock is free once its request settled
90
+ await this.lockDone;
91
+ this.lockDone = null;
92
+ }
93
+ async acquireLock() {
94
+ const locks = globalThis.navigator?.locks;
95
+ const exclusive = this.options.exclusive ?? this.options.factory === undefined;
96
+ if (!exclusive || !locks || this.releaseLock) {
97
+ return;
98
+ }
99
+ const taken = await new Promise((resolve) => {
100
+ this.lockDone = locks.request(`spinajs-outbox:${this.name}`, { ifAvailable: true }, async (lock) => {
101
+ if (!lock) {
102
+ resolve(false);
103
+ return;
104
+ }
105
+ await new Promise((release) => {
106
+ this.releaseLock = release;
107
+ resolve(true);
108
+ });
109
+ });
110
+ });
111
+ if (!taken) {
112
+ throw new Error(`outbox "${this.name}" is already open in another tab or client; only one live client per name is allowed`);
113
+ }
114
+ }
115
+ open() {
116
+ if (this.db) {
117
+ return this.db;
118
+ }
119
+ const opening = this.acquireLock().then(() => new Promise((resolve, reject) => {
120
+ const factory = this.options.factory ?? globalThis.indexedDB;
121
+ if (!factory) {
122
+ reject(new Error('IndexedDB is not available here; pass IndexedDbOutboxOptions.factory'));
123
+ return;
124
+ }
125
+ const request = factory.open(`spinajs-outbox:${this.name}`, VERSION);
126
+ request.onupgradeneeded = () => {
127
+ request.result.createObjectStore(PENDING, { keyPath: 'seq', autoIncrement: true });
128
+ request.result.createObjectStore(FAILED, { keyPath: 'seq' });
129
+ };
130
+ request.onsuccess = () => {
131
+ const db = request.result;
132
+ if (!db.objectStoreNames.contains(PENDING) || !db.objectStoreNames.contains(FAILED)) {
133
+ db.close();
134
+ reject(new Error(`database "spinajs-outbox:${this.name}" is not an outbox: missing object stores`));
135
+ return;
136
+ }
137
+ // A newer schema in another tab waits for this connection to go away; the next call reopens it.
138
+ const forget = () => {
139
+ db.close();
140
+ if (this.db === opening) {
141
+ this.db = null;
142
+ }
143
+ };
144
+ db.onversionchange = forget;
145
+ db.onclose = forget;
146
+ resolve(db);
147
+ };
148
+ request.onerror = () => reject(explain(request.error, this.name));
149
+ }));
150
+ // a failed open is tried again by the next call
151
+ opening.catch(() => {
152
+ if (this.db === opening) {
153
+ this.db = null;
154
+ }
155
+ if (!this.db) {
156
+ this.releaseLock?.();
157
+ this.releaseLock = null;
158
+ }
159
+ });
160
+ this.db = opening;
161
+ return opening;
162
+ }
163
+ async run(stores, mode, work) {
164
+ let db = await this.open();
165
+ let tx;
166
+ try {
167
+ tx = this.begin(db, stores, mode);
168
+ }
169
+ catch (err) {
170
+ if (err.name !== 'InvalidStateError') {
171
+ throw explain(err, this.name);
172
+ }
173
+ // the connection was closed behind our back: reopen once
174
+ if (this.db) {
175
+ this.db = null;
176
+ }
177
+ db = await this.open();
178
+ try {
179
+ tx = this.begin(db, stores, mode);
180
+ }
181
+ catch (again) {
182
+ throw explain(again, this.name);
183
+ }
184
+ }
185
+ return new Promise((resolve, reject) => {
186
+ let request;
187
+ try {
188
+ request = work(tx);
189
+ }
190
+ catch (err) {
191
+ // e.g. a payload structured clone cannot copy: nothing of this call may commit
192
+ try {
193
+ tx.abort();
194
+ }
195
+ catch {
196
+ // already finished
197
+ }
198
+ reject(explain(err, this.name));
199
+ return;
200
+ }
201
+ tx.oncomplete = () => resolve(request.result);
202
+ // `tx.error` is only set by the time of `abort`, never at `error`
203
+ tx.onabort = () => reject(explain(tx.error ?? request.error, this.name) ?? new Error('IndexedDB transaction aborted'));
204
+ });
205
+ }
206
+ // strict: the commit reaches the disk before `complete`, which a durable command relies on
207
+ begin(db, stores, mode) {
208
+ return db.transaction(stores, mode, { durability: 'strict' });
209
+ }
210
+ }
211
+ exports.IndexedDbOutboxStore = IndexedDbOutboxStore;
212
+ //# sourceMappingURL=outbox-indexeddb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox-indexeddb.js","sourceRoot":"","sources":["../../src/outbox-indexeddb.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { SocketError } from '@spinajs/http-socket-common';
2
+ /** A durable command waiting for its reply. */
3
+ export interface OutboxEntry<P = unknown> {
4
+ /** Queue position: assigned by the store, strictly increasing, never reused. */
5
+ readonly seq: number;
6
+ readonly name: string;
7
+ readonly payload: P;
8
+ /** Idempotency key chosen by the app; the queue persists `${clientId}:<random nonce>` when absent. */
9
+ readonly key?: string;
10
+ /** ms epoch */
11
+ readonly createdAt: number;
12
+ }
13
+ /** An entry the server refused for good; it stays until the app clears it. */
14
+ export interface FailedOutboxEntry<P = unknown> extends OutboxEntry<P> {
15
+ readonly error: SocketError;
16
+ /** ms epoch */
17
+ readonly failedAt: number;
18
+ }
19
+ export interface OutboxInput<P = unknown> {
20
+ name: string;
21
+ payload: P;
22
+ key?: string;
23
+ }
24
+ /**
25
+ * Where durable commands wait for their reply. Every method resolves only once its change is durable
26
+ * in the store's medium: the queue sends a command after `add` resolved. One SocketClient at a time per store.
27
+ */
28
+ export interface OutboxStore {
29
+ add<P>(input: OutboxInput<P>): Promise<OutboxEntry<P>>;
30
+ /** Entries not yet acknowledged, ascending `seq`. */
31
+ pending(): Promise<OutboxEntry[]>;
32
+ /** Removes an acknowledged entry; an unknown seq is ignored. */
33
+ remove(seq: number): Promise<void>;
34
+ /** Moves the pending entry `entry.seq` to the failed list as `entry`; ignored when that seq is not pending. */
35
+ fail(entry: FailedOutboxEntry): Promise<void>;
36
+ /** The failed list, ascending `seq`: export it before clearing it. */
37
+ failed(): Promise<FailedOutboxEntry[]>;
38
+ /** Removes failed entries; all of them when `seqs` is omitted. */
39
+ clearFailed(seqs?: readonly number[]): Promise<void>;
40
+ }
41
+ /** Keeps nothing across reloads: the default when no store is given. Entries are cloned in and out, as a persistent store would. */
42
+ export declare class MemoryOutboxStore implements OutboxStore {
43
+ private seq;
44
+ private readonly entries;
45
+ private readonly failures;
46
+ add<P>(input: OutboxInput<P>): Promise<OutboxEntry<P>>;
47
+ pending(): Promise<OutboxEntry[]>;
48
+ remove(seq: number): Promise<void>;
49
+ fail(entry: FailedOutboxEntry): Promise<void>;
50
+ failed(): Promise<FailedOutboxEntry[]>;
51
+ clearFailed(seqs?: readonly number[]): Promise<void>;
52
+ }
53
+ //# sourceMappingURL=outbox.d.ts.map