@synapseone/sdk 0.0.0-stage → 0.7.0-dev.43

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.
@@ -0,0 +1,666 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ /*
7
+ * Copyright 2026 SynapseOne Contributors
8
+ * Licensed under the Apache License, Version 2.0 (the "License");
9
+ * Offline tests for the split service SDKs — local mock gateway, no platform.
10
+ *
11
+ * Covers the base/faststore/queue namespaces, the typed BaseCollection
12
+ * object<->data mapping, tenant headers and the deprecated flat wrappers.
13
+ */
14
+ const node_test_1 = require("node:test");
15
+ const strict_1 = __importDefault(require("node:assert/strict"));
16
+ const node_http_1 = __importDefault(require("node:http"));
17
+ const index_1 = require("./index");
18
+ let base = '';
19
+ let lastHeaders = {};
20
+ let lastPath = '';
21
+ let lastMethod = '';
22
+ let lastBody = '';
23
+ function payload(method, path, body) {
24
+ const p = path.split('?')[0];
25
+ if (method === 'GET' && (p === '/v1/base/collections' || p === '/v1/lds/licenses'))
26
+ return [];
27
+ if (method === 'GET' && p === '/v1/base/collections/parks/documents') {
28
+ return [{
29
+ id: 'd1',
30
+ data: { profile: { name: 'Central', capacity: 10 } },
31
+ version: 1,
32
+ createdAt: 't',
33
+ updatedAt: 't',
34
+ }];
35
+ }
36
+ if (method === 'GET' && p === '/v1/base/collections/parks/documents/d1') {
37
+ return {
38
+ id: 'd1',
39
+ object: { name: 'Central', capacity: 10 },
40
+ data: { profile: { name: 'Central', capacity: 10 } },
41
+ version: 3,
42
+ createdAt: 't',
43
+ updatedAt: 't',
44
+ collection: 'parks',
45
+ };
46
+ }
47
+ if (p.endsWith('/query')) {
48
+ return {
49
+ items: [{
50
+ id: 'p1',
51
+ version: 1,
52
+ data: { profile: { name: 'Central', capacity: 10 } },
53
+ object: { name: 'Central', capacity: 10 },
54
+ createdAt: 't',
55
+ updatedAt: 't',
56
+ }],
57
+ total: 1,
58
+ limit: 100,
59
+ offset: 0,
60
+ nextCursor: 'abc',
61
+ };
62
+ }
63
+ if (p.endsWith('/changes')) {
64
+ return { changes: [], nextSince: '2026-01-01T00:00:00.000Z', hasMore: false };
65
+ }
66
+ if (p.endsWith('/vector-search')) {
67
+ return {
68
+ items: [{
69
+ id: 'p1',
70
+ similarity: 0.99,
71
+ distance: 0.01,
72
+ version: 1,
73
+ data: { profile: { name: 'Central', capacity: 10 } },
74
+ object: { name: 'Central', capacity: 10 },
75
+ createdAt: 't',
76
+ updatedAt: 't',
77
+ }],
78
+ total: 1,
79
+ limit: 10,
80
+ metric: 'cosine',
81
+ };
82
+ }
83
+ if (method !== 'GET' && p.startsWith('/v1/base/collections/parks/documents') && !p.endsWith(':batch')) {
84
+ return {
85
+ id: 'p1',
86
+ data: { profile: { name: 'Central', capacity: 10 } },
87
+ version: 1,
88
+ createdAt: 't',
89
+ updatedAt: 't',
90
+ };
91
+ }
92
+ if (method === 'GET' && p === '/v1/faststore/keys') {
93
+ return { keys: [], cursor: null, truncated: false };
94
+ }
95
+ if (method === 'GET' && p.startsWith('/v1/faststore/keys/') && !p.endsWith('/ttl')) {
96
+ return { key: 'user:1', type: 'string', value: 'v', ttlSeconds: 60 };
97
+ }
98
+ if (p.endsWith('/ttl'))
99
+ return { key: 'user:1', ttlSeconds: 42 };
100
+ if (method === 'GET' && p === '/v1/queue/queues')
101
+ return [];
102
+ if (p.endsWith('/consume')) {
103
+ return {
104
+ queue: 'emails',
105
+ count: 1,
106
+ visibilityTimeoutSeconds: 30,
107
+ messages: [{
108
+ id: 'm1',
109
+ payload: { to: 'a@b.c' },
110
+ attempts: 1,
111
+ maxAttempts: 5,
112
+ publishedAt: 't',
113
+ receipt: 'r1',
114
+ leaseExpiresAt: 't2',
115
+ }],
116
+ };
117
+ }
118
+ if (p.endsWith('/ack')) {
119
+ if (body && Array.isArray(body.receipts)) {
120
+ return {
121
+ queue: 'emails',
122
+ results: body.receipts.map((r) => ({ ok: true, receipt: r, id: 'm1' })),
123
+ ok: true,
124
+ };
125
+ }
126
+ return { ok: true, queue: 'emails', receipt: body?.receipt, id: 'm1' };
127
+ }
128
+ if (p.endsWith('/nack')) {
129
+ return { ok: true, queue: 'emails', id: 'm1', state: 'ready', attempts: 2 };
130
+ }
131
+ if (p.endsWith('/heartbeat')) {
132
+ return { ok: true, queue: 'emails', receipt: 'r1', leaseExpiresAt: 't3', visibilityTimeoutSeconds: 30 };
133
+ }
134
+ if (method === 'GET' && (p.endsWith('/messages') || p.endsWith('/dead'))) {
135
+ return { queue: 'emails', count: 0, messages: [] };
136
+ }
137
+ if (p.endsWith('/replay')) {
138
+ return { ok: true, queue: 'emails', message: { id: 'm1', attempts: 0 } };
139
+ }
140
+ return { ok: true };
141
+ }
142
+ (0, node_test_1.before)(() => new Promise((resolve) => {
143
+ const server = node_http_1.default.createServer((req, res) => {
144
+ lastHeaders = { ...req.headers };
145
+ lastPath = req.url || '';
146
+ lastMethod = req.method || '';
147
+ let raw = '';
148
+ req.on('data', (c) => (raw += c));
149
+ req.on('end', () => {
150
+ lastBody = raw;
151
+ let parsed;
152
+ try {
153
+ parsed = raw ? JSON.parse(raw) : undefined;
154
+ }
155
+ catch {
156
+ parsed = undefined;
157
+ }
158
+ res.setHeader('Content-Type', 'application/json');
159
+ res.end(JSON.stringify(payload(req.method || '', req.url || '', parsed)));
160
+ });
161
+ });
162
+ server.listen(0, '127.0.0.1', () => {
163
+ const a = server.address();
164
+ base = `http://127.0.0.1:${typeof a === 'object' && a ? a.port : 0}`;
165
+ // Keep the server alive for the whole suite; node exits when tests end.
166
+ server.unref();
167
+ resolve();
168
+ });
169
+ }));
170
+ (0, node_test_1.after)(() => {
171
+ /* server is unref'd so the test runner can exit cleanly */
172
+ });
173
+ (0, node_test_1.describe)('service facade', () => {
174
+ (0, node_test_1.it)('exposes every service namespace and the baseUrl getter', () => {
175
+ const c = new index_1.SynapseOneClient({ baseUrl: 'http://localhost:8080/' });
176
+ strict_1.default.equal(c.baseUrl, 'http://localhost:8080');
177
+ for (const name of ['iam', 'workflow', 'lds', 'base', 'faststore', 'queue']) {
178
+ strict_1.default.ok(c[name], name);
179
+ }
180
+ strict_1.default.ok(c.base.collection('parks') instanceof index_1.BaseCollection);
181
+ });
182
+ (0, node_test_1.it)('deprecated wrappers warn and forward to the service namespace', async () => {
183
+ const c = new index_1.SynapseOneClient({ baseUrl: base });
184
+ const messages = [];
185
+ const original = process.emitWarning;
186
+ process.emitWarning = (warning) => {
187
+ messages.push(typeof warning === 'string' ? warning : warning.message);
188
+ };
189
+ try {
190
+ await c.listTemplates();
191
+ strict_1.default.equal(lastPath, '/v1/templates');
192
+ await c.ldsLicenses();
193
+ strict_1.default.equal(lastPath, '/v1/lds/licenses');
194
+ }
195
+ finally {
196
+ process.emitWarning = original;
197
+ }
198
+ strict_1.default.ok(messages.some((m) => m.includes('client.workflow.listTemplates()')), String(messages));
199
+ strict_1.default.ok(messages.some((m) => m.includes('client.lds.licenses()')), String(messages));
200
+ });
201
+ });
202
+ (0, node_test_1.describe)('SynapseBase', () => {
203
+ (0, node_test_1.it)('collection lifecycle paths and bodies', async () => {
204
+ const c = new index_1.SynapseOneClient({ baseUrl: base });
205
+ await c.base.createCollection({
206
+ name: 'parks',
207
+ description: 'city parks',
208
+ mapping: { name: 'profile.name' },
209
+ });
210
+ strict_1.default.equal(lastPath, '/v1/base/collections');
211
+ strict_1.default.equal(lastMethod, 'POST');
212
+ strict_1.default.deepEqual(JSON.parse(lastBody), {
213
+ name: 'parks',
214
+ description: 'city parks',
215
+ mapping: { name: 'profile.name' },
216
+ });
217
+ await c.base.updateCollection('parks', { name: 'parks2', description: null });
218
+ strict_1.default.equal(lastMethod, 'PATCH');
219
+ strict_1.default.equal(lastPath, '/v1/base/collections/parks');
220
+ strict_1.default.deepEqual(JSON.parse(lastBody), { name: 'parks2', description: null });
221
+ await c.base.getCollection('parks');
222
+ strict_1.default.equal(lastPath, '/v1/base/collections/parks');
223
+ await c.base.collectionStats('parks');
224
+ strict_1.default.equal(lastPath, '/v1/base/collections/parks/stats');
225
+ await c.base.deleteCollection('parks');
226
+ strict_1.default.equal(lastMethod, 'DELETE');
227
+ });
228
+ (0, node_test_1.it)('documents accept exactly one of data or object', async () => {
229
+ const c = new index_1.SynapseOneClient({ baseUrl: base });
230
+ await c.base.createDocument('parks', { object: { name: 'Central' }, id: 'p1' });
231
+ strict_1.default.equal(lastPath, '/v1/base/collections/parks/documents');
232
+ strict_1.default.deepEqual(JSON.parse(lastBody), { object: { name: 'Central' }, id: 'p1' });
233
+ await c.base.createDocument('parks', { data: { profile: { name: 'Central' } } });
234
+ strict_1.default.deepEqual(JSON.parse(lastBody), { data: { profile: { name: 'Central' } } });
235
+ strict_1.default.throws(() => c.base.createDocument('parks', {}), /exactly one of data or object/);
236
+ strict_1.default.throws(() => c.base.createDocument('parks', { data: {}, object: {} }), /exactly one of data or object/);
237
+ await c.base.getDocument('parks', 'p1', { as: 'object' });
238
+ strict_1.default.equal(lastPath, '/v1/base/collections/parks/documents/p1?as=object');
239
+ await c.base.setDocument('parks', 'p1', { data: { a: 1 }, ifVersion: 2, merge: true });
240
+ strict_1.default.equal(lastMethod, 'PUT');
241
+ strict_1.default.deepEqual(JSON.parse(lastBody), { data: { a: 1 }, ifVersion: 2, merge: true });
242
+ await c.base.updateDocument('parks', 'p1', {
243
+ data: { b: 2 },
244
+ unset: ['profile.old'],
245
+ ifVersion: 3,
246
+ });
247
+ strict_1.default.equal(lastMethod, 'PATCH');
248
+ strict_1.default.deepEqual(JSON.parse(lastBody), {
249
+ data: { b: 2 },
250
+ unset: ['profile.old'],
251
+ ifVersion: 3,
252
+ });
253
+ strict_1.default.throws(() => c.base.updateDocument('parks', 'p1', {}), /provide data\/object/);
254
+ await c.base.deleteDocument('parks', 'p1', { ifVersion: 2 });
255
+ strict_1.default.equal(lastPath, '/v1/base/collections/parks/documents/p1?ifVersion=2');
256
+ strict_1.default.equal(lastMethod, 'DELETE');
257
+ });
258
+ (0, node_test_1.it)('query, increment, batch and changes', async () => {
259
+ const c = new index_1.SynapseOneClient({ baseUrl: base });
260
+ const result = await c.base.queryDocuments('parks', {
261
+ where: { status: 'active' },
262
+ orderBy: [{ field: 'name', direction: 'desc' }],
263
+ limit: 10,
264
+ offset: 5,
265
+ select: ['name'],
266
+ as: 'object',
267
+ });
268
+ strict_1.default.equal(lastPath, '/v1/base/collections/parks/query');
269
+ strict_1.default.deepEqual(JSON.parse(lastBody), {
270
+ as: 'object',
271
+ where: { status: 'active' },
272
+ orderBy: [{ field: 'name', direction: 'desc' }],
273
+ limit: 10,
274
+ offset: 5,
275
+ select: ['name'],
276
+ });
277
+ strict_1.default.equal(result.total, 1);
278
+ strict_1.default.equal(result.nextCursor, 'abc');
279
+ await c.base.increment('parks', 'p1', { field: 'counter', by: 3, ifVersion: 2 });
280
+ strict_1.default.equal(lastPath, '/v1/base/collections/parks/documents/p1/increment');
281
+ strict_1.default.deepEqual(JSON.parse(lastBody), { field: 'counter', by: 3, ifVersion: 2 });
282
+ const ops = [{ op: 'create', object: { name: 'x' } }];
283
+ await c.base.batch('parks', ops);
284
+ strict_1.default.equal(lastPath, '/v1/base/collections/parks/documents:batch');
285
+ strict_1.default.deepEqual(JSON.parse(lastBody), { operations: ops });
286
+ const out = await c.base.changes('parks', {
287
+ since: '2026-01-01T00:00:00Z',
288
+ limit: 50,
289
+ as: 'object',
290
+ });
291
+ const changedPath = lastPath;
292
+ strict_1.default.ok(changedPath.startsWith('/v1/base/collections/parks/changes?'), changedPath);
293
+ strict_1.default.ok(changedPath.includes('since=2026-01-01T00%3A00%3A00Z'), changedPath);
294
+ strict_1.default.ok(changedPath.includes('limit=50'), changedPath);
295
+ strict_1.default.ok(changedPath.includes('as=object'), changedPath);
296
+ strict_1.default.equal(out.nextSince, '2026-01-01T00:00:00.000Z');
297
+ await c.base.listDocuments('parks', { limit: 10, offset: 20 });
298
+ strict_1.default.equal(lastPath, '/v1/base/collections/parks/documents?limit=10&offset=20');
299
+ });
300
+ (0, node_test_1.it)('mapping helpers project both directions', () => {
301
+ const data = { profile: { name: 'Central', capacity: 10 } };
302
+ const mapping = {
303
+ name: 'profile.name',
304
+ capacity: { path: 'profile.capacity', type: 'number' },
305
+ };
306
+ strict_1.default.deepEqual((0, index_1.dataToObject)(data, mapping), { name: 'Central', capacity: 10 });
307
+ strict_1.default.deepEqual((0, index_1.objectToData)({ name: 'Central' }, mapping), { profile: { name: 'Central' } });
308
+ strict_1.default.deepEqual((0, index_1.dataToObject)(data), data);
309
+ strict_1.default.deepEqual((0, index_1.objectToData)({ any: 1 }), { any: 1 });
310
+ });
311
+ (0, node_test_1.it)('typed BaseCollection converts models, writes objects and reads data', async () => {
312
+ class Park {
313
+ name;
314
+ capacity;
315
+ constructor(name, capacity) {
316
+ this.name = name;
317
+ this.capacity = capacity;
318
+ }
319
+ }
320
+ const c = new index_1.SynapseOneClient({ baseUrl: base });
321
+ const col = c.base.collection('parks', {
322
+ model: (obj) => new Park(String(obj.name), Number(obj.capacity)),
323
+ mapping: { name: 'profile.name', capacity: 'profile.capacity' },
324
+ });
325
+ strict_1.default.deepEqual({ ...col.toObject(new Park('Central', 10)) }, { name: 'Central', capacity: 10 });
326
+ strict_1.default.deepEqual(col.fromObject({ name: 'Central', capacity: 10 }), new Park('Central', 10));
327
+ strict_1.default.deepEqual(col.fromData({ profile: { name: 'Central', capacity: 10 } }), new Park('Central', 10));
328
+ const created = await col.create(new Park('Central', 10), { id: 'p1' });
329
+ strict_1.default.deepEqual(JSON.parse(lastBody), {
330
+ object: { name: 'Central', capacity: 10 },
331
+ id: 'p1',
332
+ });
333
+ strict_1.default.deepEqual(created, new Park('Central', 10));
334
+ const fetched = await col.get('d1');
335
+ strict_1.default.equal(lastPath, '/v1/base/collections/parks/documents/d1?as=object');
336
+ strict_1.default.deepEqual(fetched, new Park('Central', 10));
337
+ const listed = await col.list({ limit: 5 });
338
+ strict_1.default.equal(lastPath, '/v1/base/collections/parks/documents?limit=5');
339
+ strict_1.default.deepEqual(listed, [new Park('Central', 10)]);
340
+ const queried = await col.query({ where: { name: 'Central' } });
341
+ strict_1.default.equal(JSON.parse(lastBody).as, 'object');
342
+ strict_1.default.deepEqual(queried.items, [new Park('Central', 10)]);
343
+ const updated = await col.update('d1', new Park('Central', 11), { unset: ['profile.old'] });
344
+ strict_1.default.deepEqual(JSON.parse(lastBody), {
345
+ object: { name: 'Central', capacity: 11 },
346
+ unset: ['profile.old'],
347
+ });
348
+ strict_1.default.deepEqual(updated, new Park('Central', 10));
349
+ strict_1.default.throws(() => col.create(), /exactly one of value or data/);
350
+ strict_1.default.throws(() => col.create(new Park('x', 1), { data: { name: 'x' } }), /exactly one of value or data/);
351
+ const raw = await col.getDocument('d1', 'data');
352
+ strict_1.default.equal(lastPath, '/v1/base/collections/parks/documents/d1?as=data');
353
+ strict_1.default.ok(raw.data);
354
+ const vsRes = await c.base.vectorSearch('parks', {
355
+ vector: [0.1, 0.2, 0.3],
356
+ limit: 5,
357
+ metric: 'cosine',
358
+ where: { name: 'Central' },
359
+ });
360
+ strict_1.default.equal(lastPath, '/v1/base/collections/parks/vector-search');
361
+ strict_1.default.equal(lastMethod, 'POST');
362
+ strict_1.default.deepEqual(JSON.parse(lastBody), {
363
+ vector: [0.1, 0.2, 0.3],
364
+ as: 'data',
365
+ limit: 5,
366
+ metric: 'cosine',
367
+ where: { name: 'Central' },
368
+ });
369
+ strict_1.default.equal(vsRes.items.length, 1);
370
+ strict_1.default.equal(vsRes.items[0].id, 'p1');
371
+ strict_1.default.equal(vsRes.items[0].similarity, 0.99);
372
+ const colVs = await col.vectorSearch({ vector: [0.1, 0.2, 0.3], limit: 5 });
373
+ strict_1.default.equal(colVs.items.length, 1);
374
+ strict_1.default.deepEqual(colVs.items[0].object, new Park('Central', 10));
375
+ });
376
+ });
377
+ (0, node_test_1.describe)('Synapse Faststore', () => {
378
+ (0, node_test_1.it)('key/value bodies and the tenant header', async () => {
379
+ const c = new index_1.SynapseOneClient({ baseUrl: base });
380
+ c.setTenant('acme');
381
+ await c.faststore.set('user:1', { a: 1 }, { ttlSeconds: 60, ifNotExists: true });
382
+ strict_1.default.equal(lastPath, '/v1/faststore/keys/user%3A1');
383
+ strict_1.default.equal(lastMethod, 'PUT');
384
+ strict_1.default.equal(lastHeaders['x-synapseone-tenant'], 'acme');
385
+ strict_1.default.deepEqual(JSON.parse(lastBody), {
386
+ value: { a: 1 },
387
+ ttlSeconds: 60,
388
+ ifNotExists: true,
389
+ });
390
+ const got = await c.faststore.get('user:1');
391
+ strict_1.default.equal(got.value, 'v');
392
+ await c.faststore.expire('user:1', 30);
393
+ strict_1.default.equal(lastPath, '/v1/faststore/keys/user%3A1/expire');
394
+ strict_1.default.deepEqual(JSON.parse(lastBody), { ttlSeconds: 30 });
395
+ await c.faststore.incr('counter', { by: 2 });
396
+ strict_1.default.deepEqual(JSON.parse(lastBody), { by: 2 });
397
+ const ttl = await c.faststore.ttl('user:1');
398
+ strict_1.default.equal(ttl.ttlSeconds, 42);
399
+ await c.faststore.listKeys({ prefix: 'user:', limit: 5, cursor: '10' });
400
+ strict_1.default.equal(lastPath, '/v1/faststore/keys?prefix=user%3A&limit=5&cursor=10');
401
+ const ops = [{ op: 'set', key: 'a', value: 1 }];
402
+ await c.faststore.batch(ops);
403
+ strict_1.default.deepEqual(JSON.parse(lastBody), { operations: ops });
404
+ await c.faststore.delete('user:1');
405
+ strict_1.default.equal(lastMethod, 'DELETE');
406
+ c.setTenant(undefined);
407
+ });
408
+ (0, node_test_1.it)('collections and locks', async () => {
409
+ const c = new index_1.SynapseOneClient({ baseUrl: base });
410
+ await c.faststore.hashSet('h1', { a: 1 }, { ttlSeconds: 10 });
411
+ strict_1.default.equal(lastPath, '/v1/faststore/hashes/h1');
412
+ strict_1.default.deepEqual(JSON.parse(lastBody), { values: { a: 1 }, ttlSeconds: 10 });
413
+ await c.faststore.hashGetField('h1', 'a');
414
+ strict_1.default.equal(lastPath, '/v1/faststore/hashes/h1/a');
415
+ await c.faststore.hashIncr('h1', 'a', { by: 2 });
416
+ strict_1.default.equal(lastPath, '/v1/faststore/hashes/h1/incr');
417
+ strict_1.default.deepEqual(JSON.parse(lastBody), { field: 'a', by: 2 });
418
+ await c.faststore.hashLength('h1');
419
+ strict_1.default.equal(lastPath, '/v1/faststore/hashes/h1/length');
420
+ await c.faststore.hashDeleteField('h1', 'a');
421
+ strict_1.default.equal(lastMethod, 'DELETE');
422
+ strict_1.default.equal(lastPath, '/v1/faststore/hashes/h1/a');
423
+ await c.faststore.listPush('l1', ['x', 'y'], { side: 'left' });
424
+ strict_1.default.deepEqual(JSON.parse(lastBody), { values: ['x', 'y'], side: 'left' });
425
+ await c.faststore.listPop('l1', { side: 'right', count: 2 });
426
+ strict_1.default.deepEqual(JSON.parse(lastBody), { side: 'right', count: 2 });
427
+ await c.faststore.listRange('l1', 0, -1);
428
+ strict_1.default.equal(lastPath, '/v1/faststore/lists/l1?start=0&stop=-1');
429
+ await c.faststore.listTrim('l1', 0, 9);
430
+ strict_1.default.deepEqual(JSON.parse(lastBody), { start: 0, stop: 9 });
431
+ await c.faststore.setAdd('s1', ['a'], { ttlSeconds: 5 });
432
+ strict_1.default.deepEqual(JSON.parse(lastBody), { members: ['a'], ttlSeconds: 5 });
433
+ await c.faststore.setContains('s1', 'a');
434
+ strict_1.default.deepEqual(JSON.parse(lastBody), { member: 'a' });
435
+ await c.faststore.setRemove('s1', ['a']);
436
+ strict_1.default.deepEqual(JSON.parse(lastBody), { members: ['a'] });
437
+ await c.faststore.setCard('s1');
438
+ strict_1.default.equal(lastPath, '/v1/faststore/sets/s1/card');
439
+ await c.faststore.zsetAdd('z1', [{ member: 'a', score: 2 }]);
440
+ strict_1.default.deepEqual(JSON.parse(lastBody), { members: [{ member: 'a', score: 2 }] });
441
+ await c.faststore.zsetRange('z1', { start: 0, stop: -1, rev: true, withScores: true });
442
+ strict_1.default.equal(lastPath, '/v1/faststore/zsets/z1?start=0&stop=-1&rev=true&withScores=true');
443
+ await c.faststore.zsetIncr('z1', 'a', { by: 1.5 });
444
+ strict_1.default.deepEqual(JSON.parse(lastBody), { member: 'a', by: 1.5 });
445
+ await c.faststore.zsetRank('z1', 'a', { rev: true });
446
+ strict_1.default.deepEqual(JSON.parse(lastBody), { member: 'a', rev: true });
447
+ await c.faststore.lockAcquire('job', { ttlSeconds: 60, waitMs: 500, token: 'tok' });
448
+ strict_1.default.equal(lastPath, '/v1/faststore/locks/job/acquire');
449
+ strict_1.default.deepEqual(JSON.parse(lastBody), { waitMs: 500, ttlSeconds: 60, token: 'tok' });
450
+ await c.faststore.lockExtend('job', 'tok', { ttlSeconds: 60 });
451
+ strict_1.default.deepEqual(JSON.parse(lastBody), { token: 'tok', ttlSeconds: 60 });
452
+ await c.faststore.lockRelease('job', 'tok');
453
+ strict_1.default.deepEqual(JSON.parse(lastBody), { token: 'tok' });
454
+ });
455
+ });
456
+ (0, node_test_1.describe)('SynapseQueue', () => {
457
+ (0, node_test_1.it)('queue config and publish', async () => {
458
+ const c = new index_1.SynapseOneClient({ baseUrl: base });
459
+ await c.queue.createQueue('emails', { maxAttempts: 7, visibilityTimeoutSeconds: 45 });
460
+ strict_1.default.equal(lastPath, '/v1/queue/queues/emails');
461
+ strict_1.default.equal(lastMethod, 'PUT');
462
+ strict_1.default.deepEqual(JSON.parse(lastBody), { maxAttempts: 7, visibilityTimeoutSeconds: 45 });
463
+ await c.queue.listQueues();
464
+ strict_1.default.equal(lastPath, '/v1/queue/queues');
465
+ await c.queue.publish('emails', { to: 'a@b.c' }, {
466
+ delaySeconds: 30,
467
+ maxAttempts: 3,
468
+ headers: { trace: 't1' },
469
+ id: 'm1',
470
+ dedupeKey: 'welcome:a@b.c',
471
+ });
472
+ strict_1.default.equal(lastPath, '/v1/queue/queues/emails/messages');
473
+ strict_1.default.deepEqual(JSON.parse(lastBody), {
474
+ payload: { to: 'a@b.c' },
475
+ delaySeconds: 30,
476
+ maxAttempts: 3,
477
+ headers: { trace: 't1' },
478
+ id: 'm1',
479
+ dedupeKey: 'welcome:a@b.c',
480
+ });
481
+ const msgs = [{ payload: 1 }, { payload: 2, dedupeKey: 'k' }];
482
+ await c.queue.publishBatch('emails', msgs);
483
+ strict_1.default.equal(lastPath, '/v1/queue/queues/emails/messages:batch');
484
+ strict_1.default.deepEqual(JSON.parse(lastBody), { messages: msgs });
485
+ await c.queue.queueStats('emails');
486
+ strict_1.default.equal(lastPath, '/v1/queue/queues/emails/stats');
487
+ await c.queue.purgeQueue('emails');
488
+ strict_1.default.equal(lastPath, '/v1/queue/queues/emails/purge');
489
+ await c.queue.deleteQueue('emails');
490
+ strict_1.default.equal(lastMethod, 'DELETE');
491
+ });
492
+ (0, node_test_1.it)('consume, ack, nack and heartbeat', async () => {
493
+ const c = new index_1.SynapseOneClient({ baseUrl: base });
494
+ const out = await c.queue.consume('emails', {
495
+ maxMessages: 5,
496
+ visibilityTimeoutSeconds: 20,
497
+ consumer: 'w1',
498
+ });
499
+ strict_1.default.equal(lastPath, '/v1/queue/queues/emails/consume');
500
+ strict_1.default.deepEqual(JSON.parse(lastBody), {
501
+ maxMessages: 5,
502
+ visibilityTimeoutSeconds: 20,
503
+ consumer: 'w1',
504
+ });
505
+ strict_1.default.equal(out.count, 1);
506
+ const receipt = out.messages[0].receipt;
507
+ strict_1.default.equal(receipt, 'r1');
508
+ await c.queue.ack('emails', receipt);
509
+ strict_1.default.deepEqual(JSON.parse(lastBody), { receipt: 'r1' });
510
+ await c.queue.ackMany('emails', ['r1', 'r2']);
511
+ strict_1.default.deepEqual(JSON.parse(lastBody), { receipts: ['r1', 'r2'] });
512
+ await c.queue.nack('emails', 'r1', { requeue: false, delaySeconds: 5, error: 'boom' });
513
+ strict_1.default.equal(lastPath, '/v1/queue/queues/emails/nack');
514
+ strict_1.default.deepEqual(JSON.parse(lastBody), {
515
+ receipt: 'r1',
516
+ requeue: false,
517
+ delaySeconds: 5,
518
+ error: 'boom',
519
+ });
520
+ await c.queue.heartbeat('emails', 'r1', { visibilityTimeoutSeconds: 60 });
521
+ strict_1.default.deepEqual(JSON.parse(lastBody), {
522
+ receipt: 'r1',
523
+ visibilityTimeoutSeconds: 60,
524
+ });
525
+ });
526
+ (0, node_test_1.it)('inspection and dead letters', async () => {
527
+ const c = new index_1.SynapseOneClient({ baseUrl: base });
528
+ await c.queue.peek('emails', { limit: 10, ready: true });
529
+ strict_1.default.equal(lastPath, '/v1/queue/queues/emails/messages?limit=10&ready=true');
530
+ await c.queue.deadLetters('emails', { limit: 5 });
531
+ strict_1.default.equal(lastPath, '/v1/queue/queues/emails/dead?limit=5');
532
+ await c.queue.replayDead('emails', 'm1', { delaySeconds: 0, resetAttempts: true });
533
+ strict_1.default.equal(lastPath, '/v1/queue/queues/emails/dead/m1/replay');
534
+ strict_1.default.deepEqual(JSON.parse(lastBody), { delaySeconds: 0, resetAttempts: true });
535
+ await c.queue.deleteDead('emails', 'm1');
536
+ strict_1.default.equal(lastMethod, 'DELETE');
537
+ strict_1.default.equal(lastPath, '/v1/queue/queues/emails/dead/m1');
538
+ });
539
+ });
540
+ (0, node_test_1.describe)('SynapseStream', () => {
541
+ (0, node_test_1.it)('topics, publish, read, consumer groups and range', async () => {
542
+ const c = new index_1.SynapseOneClient({ baseUrl: base });
543
+ await c.stream.createTopic('telemetry', { maxLen: 5000 });
544
+ strict_1.default.equal(lastPath, '/v1/stream/topics/telemetry');
545
+ strict_1.default.equal(lastMethod, 'PUT');
546
+ strict_1.default.deepEqual(JSON.parse(lastBody), { maxLen: 5000 });
547
+ await c.stream.listTopics();
548
+ strict_1.default.equal(lastPath, '/v1/stream/topics');
549
+ await c.stream.getTopic('telemetry');
550
+ strict_1.default.equal(lastPath, '/v1/stream/topics/telemetry');
551
+ await c.stream.topicStats('telemetry');
552
+ strict_1.default.equal(lastPath, '/v1/stream/topics/telemetry/stats');
553
+ await c.stream.publish('telemetry', { temp: 24.5 }, { key: 's1' });
554
+ strict_1.default.equal(lastPath, '/v1/stream/topics/telemetry/events');
555
+ strict_1.default.deepEqual(JSON.parse(lastBody), { payload: { temp: 24.5 }, key: 's1' });
556
+ await c.stream.publishBatch('telemetry', [{ payload: { a: 1 } }, { payload: { a: 2 } }]);
557
+ strict_1.default.equal(lastPath, '/v1/stream/topics/telemetry/events:batch');
558
+ await c.stream.read('telemetry', { fromId: '0-0', count: 10 });
559
+ strict_1.default.equal(lastPath, '/v1/stream/topics/telemetry/read');
560
+ strict_1.default.deepEqual(JSON.parse(lastBody), { fromId: '0-0', count: 10 });
561
+ await c.stream.range('telemetry', { start: '0-0', end: '+', count: 5 });
562
+ strict_1.default.equal(lastPath, '/v1/stream/topics/telemetry/range?start=0-0&end=%2B&count=5');
563
+ await c.stream.createGroup('telemetry', 'workers', { startId: '$' });
564
+ strict_1.default.equal(lastPath, '/v1/stream/topics/telemetry/groups/workers');
565
+ strict_1.default.equal(lastMethod, 'PUT');
566
+ strict_1.default.deepEqual(JSON.parse(lastBody), { startId: '$' });
567
+ await c.stream.readGroup('telemetry', 'workers', { consumer: 'w1', count: 5 });
568
+ strict_1.default.equal(lastPath, '/v1/stream/topics/telemetry/groups/workers/read');
569
+ await c.stream.ack('telemetry', 'workers', ['id-1', 'id-2']);
570
+ strict_1.default.equal(lastPath, '/v1/stream/topics/telemetry/groups/workers/ack');
571
+ strict_1.default.deepEqual(JSON.parse(lastBody), { ids: ['id-1', 'id-2'] });
572
+ await c.stream.destroyGroup('telemetry', 'workers');
573
+ strict_1.default.equal(lastPath, '/v1/stream/topics/telemetry/groups/workers');
574
+ strict_1.default.equal(lastMethod, 'DELETE');
575
+ await c.stream.deleteTopic('telemetry');
576
+ strict_1.default.equal(lastPath, '/v1/stream/topics/telemetry');
577
+ strict_1.default.equal(lastMethod, 'DELETE');
578
+ });
579
+ });
580
+ (0, node_test_1.describe)('Synapse Inference Engine', () => {
581
+ (0, node_test_1.it)('covers health, chat, embeddings, decision and routing paths', async () => {
582
+ const hits = [];
583
+ const server = node_http_1.default.createServer((req, res) => {
584
+ let raw = '';
585
+ req.on('data', (c) => { raw += c; });
586
+ req.on('end', () => {
587
+ hits.push({
588
+ method: req.method || 'GET',
589
+ path: req.url || '/',
590
+ body: raw ? JSON.parse(raw) : null,
591
+ });
592
+ res.setHeader('Content-Type', 'application/json');
593
+ if (req.url === '/v1/inference/health') {
594
+ res.end(JSON.stringify({ status: 'ok', service: 'inference' }));
595
+ }
596
+ else if (req.url === '/v1/inference/chat/completions') {
597
+ res.end(JSON.stringify({ id: 'chat-1', choices: [{ message: { role: 'assistant', content: 'pong' } }] }));
598
+ }
599
+ else if (req.url === '/v1/inference/embeddings') {
600
+ res.end(JSON.stringify({ object: 'list', data: [{ index: 0, embedding: [0.1, 0.2] }] }));
601
+ }
602
+ else if (req.url === '/v1/inference/decision') {
603
+ res.end(JSON.stringify({ model: 'decision-0.8B', results: { team: { best_choice: 'support' } } }));
604
+ }
605
+ else if (req.url === '/v1/inference/decision/route') {
606
+ res.end(JSON.stringify({ best_choice: 'workflow', confidence: 0.95 }));
607
+ }
608
+ else {
609
+ res.statusCode = 404;
610
+ res.end(JSON.stringify({ error: 'not_found' }));
611
+ }
612
+ });
613
+ });
614
+ await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
615
+ const a = server.address();
616
+ const url = `http://127.0.0.1:${typeof a === 'object' && a ? a.port : 0}`;
617
+ try {
618
+ const c = new index_1.SynapseOneClient({ baseUrl: url });
619
+ const h = await c.inference.health();
620
+ strict_1.default.equal(h.service, 'inference');
621
+ const chat = await c.inference.chat([{ role: 'user', content: 'ping' }]);
622
+ strict_1.default.equal(chat.choices[0].message.content, 'pong');
623
+ const emb = await c.inference.embeddings('hello');
624
+ strict_1.default.equal(emb.data.length, 1);
625
+ const dec = await c.inference.decision('test state', { team: { type: 'choice', instructions: 'pick' } });
626
+ strict_1.default.equal(dec.results.team.best_choice, 'support');
627
+ const r = await c.inference.route('run workflow', { workflow: 'DAGs', iam: 'Users' });
628
+ strict_1.default.equal(r.best_choice, 'workflow');
629
+ strict_1.default.equal(hits.length, 5);
630
+ strict_1.default.equal(hits[0].path, '/v1/inference/health');
631
+ strict_1.default.equal(hits[1].path, '/v1/inference/chat/completions');
632
+ strict_1.default.equal(hits[2].path, '/v1/inference/embeddings');
633
+ strict_1.default.equal(hits[3].path, '/v1/inference/decision');
634
+ strict_1.default.equal(hits[4].path, '/v1/inference/decision/route');
635
+ }
636
+ finally {
637
+ await new Promise((resolve) => server.close(() => resolve()));
638
+ }
639
+ });
640
+ });
641
+ (0, node_test_1.describe)('error mapping', () => {
642
+ (0, node_test_1.it)('carries status and parsed body', async () => {
643
+ const server = node_http_1.default.createServer((_req, res) => {
644
+ res.statusCode = 404;
645
+ res.setHeader('Content-Type', 'application/json');
646
+ res.end(JSON.stringify({ error: 'boom' }));
647
+ });
648
+ await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
649
+ const a = server.address();
650
+ const url = `http://127.0.0.1:${typeof a === 'object' && a ? a.port : 0}`;
651
+ try {
652
+ const c = new index_1.SynapseOneClient({ baseUrl: url });
653
+ await strict_1.default.rejects(() => c.base.getCollection('parks'), (err) => {
654
+ strict_1.default.ok(err instanceof index_1.SynapseOneError);
655
+ strict_1.default.equal(err.status, 404);
656
+ strict_1.default.deepEqual(err.body, { error: 'boom' });
657
+ strict_1.default.match(String(err.message), /synapseone 404/);
658
+ return true;
659
+ });
660
+ strict_1.default.equal(await c.faststore.exists('missing'), false);
661
+ }
662
+ finally {
663
+ await new Promise((resolve) => server.close(() => resolve()));
664
+ }
665
+ });
666
+ });