@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.
- package/LICENSE +199 -0
- package/README.md +64 -2
- package/dist/_http.d.ts +42 -0
- package/dist/_http.js +108 -0
- package/dist/ai.d.ts +30 -0
- package/dist/ai.js +36 -0
- package/dist/base.d.ts +253 -0
- package/dist/base.js +339 -0
- package/dist/client.test.d.ts +1 -0
- package/dist/client.test.js +292 -0
- package/dist/example.d.ts +1 -0
- package/dist/example.js +16 -0
- package/dist/faststore.d.ts +108 -0
- package/dist/faststore.js +201 -0
- package/dist/iam.d.ts +88 -0
- package/dist/iam.js +149 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +143 -0
- package/dist/inference.d.ts +110 -0
- package/dist/inference.js +62 -0
- package/dist/lds.d.ts +82 -0
- package/dist/lds.js +77 -0
- package/dist/queue.d.ts +72 -0
- package/dist/queue.js +129 -0
- package/dist/services.test.d.ts +1 -0
- package/dist/services.test.js +666 -0
- package/dist/stream.d.ts +99 -0
- package/dist/stream.js +117 -0
- package/dist/workflow.d.ts +82 -0
- package/dist/workflow.js +185 -0
- package/package.json +81 -4
|
@@ -0,0 +1,292 @@
|
|
|
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 SDK tests — local echo server, no platform needed.
|
|
10
|
+
*/
|
|
11
|
+
const node_test_1 = require("node:test");
|
|
12
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
13
|
+
const node_http_1 = __importDefault(require("node:http"));
|
|
14
|
+
const index_1 = require("./index");
|
|
15
|
+
let base = '';
|
|
16
|
+
let lastHeaders = {};
|
|
17
|
+
let lastPath = '';
|
|
18
|
+
let lastMethod = '';
|
|
19
|
+
let lastBody = '';
|
|
20
|
+
let server;
|
|
21
|
+
(0, node_test_1.before)(() => new Promise((resolve) => {
|
|
22
|
+
server = node_http_1.default
|
|
23
|
+
.createServer((req, res) => {
|
|
24
|
+
lastHeaders = { ...req.headers };
|
|
25
|
+
lastPath = req.url || '';
|
|
26
|
+
lastMethod = req.method || '';
|
|
27
|
+
let body = '';
|
|
28
|
+
req.on('data', (c) => (body += c));
|
|
29
|
+
req.on('end', () => {
|
|
30
|
+
lastBody = body;
|
|
31
|
+
res.setHeader('Content-Type', 'application/json');
|
|
32
|
+
res.end(JSON.stringify({ ok: true, path: req.url }));
|
|
33
|
+
});
|
|
34
|
+
})
|
|
35
|
+
.listen(0, '127.0.0.1', () => {
|
|
36
|
+
const a = server.address();
|
|
37
|
+
base = `http://127.0.0.1:${typeof a === 'object' && a ? a.port : 0}`;
|
|
38
|
+
resolve();
|
|
39
|
+
});
|
|
40
|
+
}));
|
|
41
|
+
(0, node_test_1.after)(() => new Promise((resolve) => server.close(() => resolve())));
|
|
42
|
+
(0, node_test_1.describe)('SynapseOneClient', () => {
|
|
43
|
+
(0, node_test_1.it)('keeps the SynapseDX alias identical during the rebrand window', () => {
|
|
44
|
+
strict_1.default.equal(index_1.SynapseDXClient, index_1.SynapseOneClient);
|
|
45
|
+
strict_1.default.equal(index_1.SynapseDXError, index_1.SynapseOneError);
|
|
46
|
+
});
|
|
47
|
+
(0, node_test_1.it)('trims trailing slash', () => {
|
|
48
|
+
const c = new index_1.SynapseOneClient({ baseUrl: 'http://x:8080/' });
|
|
49
|
+
strict_1.default.equal(c.baseUrl, 'http://x:8080');
|
|
50
|
+
});
|
|
51
|
+
(0, node_test_1.it)('sends Bearer token', async () => {
|
|
52
|
+
const c = new index_1.SynapseOneClient({ baseUrl: base });
|
|
53
|
+
c.setToken('tok123');
|
|
54
|
+
await c.listOrgs();
|
|
55
|
+
strict_1.default.equal(lastHeaders['authorization'], 'Bearer tok123');
|
|
56
|
+
strict_1.default.equal(lastPath, '/v1/orgs');
|
|
57
|
+
});
|
|
58
|
+
(0, node_test_1.it)('sends X-API-Key', async () => {
|
|
59
|
+
const c = new index_1.SynapseOneClient({ baseUrl: base, apiKey: 'k1' });
|
|
60
|
+
await c.listTemplates();
|
|
61
|
+
strict_1.default.equal(lastHeaders['x-api-key'], 'k1');
|
|
62
|
+
});
|
|
63
|
+
(0, node_test_1.it)('throws rich error on non-2xx', async () => {
|
|
64
|
+
const bad = new index_1.SynapseOneClient({ baseUrl: 'http://127.0.0.1:1' });
|
|
65
|
+
await strict_1.default.rejects(() => bad.listOrgs(), /synapseone/);
|
|
66
|
+
});
|
|
67
|
+
(0, node_test_1.it)('encodes ids in paths', async () => {
|
|
68
|
+
const c = new index_1.SynapseOneClient({ baseUrl: base });
|
|
69
|
+
await c.getWorkflow('a/b');
|
|
70
|
+
strict_1.default.equal(lastPath, '/v1/workflows/a%2Fb');
|
|
71
|
+
});
|
|
72
|
+
(0, node_test_1.it)('covers new endpoints', async () => {
|
|
73
|
+
const c = new index_1.SynapseOneClient({ baseUrl: base });
|
|
74
|
+
await c.userPolicies('u1');
|
|
75
|
+
strict_1.default.equal(lastPath, '/v1/users/u1/policies');
|
|
76
|
+
await c.getTemplate('webhook-to-log');
|
|
77
|
+
strict_1.default.equal(lastPath, '/v1/templates/webhook-to-log');
|
|
78
|
+
await c.listExecutions('limit=5');
|
|
79
|
+
strict_1.default.equal(lastPath, '/v1/executions?limit=5');
|
|
80
|
+
await c.executeWorkflow('w1', {});
|
|
81
|
+
strict_1.default.equal(lastPath, '/v1/workflows/w1/execute');
|
|
82
|
+
await c.updateWorkflow('w1', { name: 'renamed' });
|
|
83
|
+
strict_1.default.equal(lastPath, '/v1/workflows/w1');
|
|
84
|
+
await c.deleteWorkflow('w1');
|
|
85
|
+
strict_1.default.equal(lastPath, '/v1/workflows/w1');
|
|
86
|
+
await c.detachPolicy('r1', 'p1');
|
|
87
|
+
strict_1.default.equal(lastPath, '/v1/roles/r1/policies/p1');
|
|
88
|
+
await c.rolePolicies('r1');
|
|
89
|
+
strict_1.default.equal(lastPath, '/v1/roles/r1/policies');
|
|
90
|
+
await c.unassignRole('u1', 'r1');
|
|
91
|
+
strict_1.default.equal(lastPath, '/v1/users/u1/roles/r1');
|
|
92
|
+
await c.userRoles('u1');
|
|
93
|
+
strict_1.default.equal(lastPath, '/v1/users/u1/roles');
|
|
94
|
+
await c.listApiKeys('a1');
|
|
95
|
+
strict_1.default.equal(lastPath, '/v1/accounts/a1/keys');
|
|
96
|
+
await c.verifyApiKey('k');
|
|
97
|
+
strict_1.default.equal(lastPath, '/v1/keys/verify');
|
|
98
|
+
await c.deactivateWorkflow('w1');
|
|
99
|
+
strict_1.default.equal(lastPath, '/v1/workflows/w1/deactivate');
|
|
100
|
+
});
|
|
101
|
+
(0, node_test_1.it)('serviceAccountToken omits scopes unless explicitly given', async () => {
|
|
102
|
+
const c = new index_1.SynapseOneClient({ baseUrl: base });
|
|
103
|
+
await c.serviceAccountToken('sa1');
|
|
104
|
+
strict_1.default.equal(lastPath, '/v1/service-accounts/sa1/token');
|
|
105
|
+
strict_1.default.deepEqual(JSON.parse(lastBody), {});
|
|
106
|
+
await c.serviceAccountToken('sa1', ['workflow:Execute']);
|
|
107
|
+
strict_1.default.deepEqual(JSON.parse(lastBody), { scopes: ['workflow:Execute'] });
|
|
108
|
+
await c.serviceAccountToken('sa1', []);
|
|
109
|
+
strict_1.default.deepEqual(JSON.parse(lastBody), { scopes: [] });
|
|
110
|
+
});
|
|
111
|
+
(0, node_test_1.it)('testRun posts definition+input', async () => {
|
|
112
|
+
const c = new index_1.SynapseOneClient({ baseUrl: base });
|
|
113
|
+
const def = { nodes: [], connections: {} };
|
|
114
|
+
await c.testRun(def, { value: 21 });
|
|
115
|
+
strict_1.default.equal(lastPath, '/v1/workflows/test-run');
|
|
116
|
+
strict_1.default.equal(lastMethod, 'POST');
|
|
117
|
+
strict_1.default.deepEqual(JSON.parse(lastBody), { definition: def, input: { value: 21 } });
|
|
118
|
+
});
|
|
119
|
+
(0, node_test_1.it)('listExecutions accepts the allowlisted dict form', async () => {
|
|
120
|
+
const c = new index_1.SynapseOneClient({ baseUrl: base });
|
|
121
|
+
await c.listExecutions({ workflowId: 'w1', status: 'error', limit: 20, offset: 0, includeData: true });
|
|
122
|
+
strict_1.default.ok(lastPath.startsWith('/v1/executions?'), lastPath);
|
|
123
|
+
strict_1.default.ok(lastPath.includes('workflowId=w1'), lastPath);
|
|
124
|
+
strict_1.default.ok(lastPath.includes('status=error'), lastPath);
|
|
125
|
+
strict_1.default.ok(lastPath.includes('limit=20'), lastPath);
|
|
126
|
+
strict_1.default.ok(lastPath.includes('includeData=true'), lastPath);
|
|
127
|
+
});
|
|
128
|
+
(0, node_test_1.it)('getExecution supports includeData', async () => {
|
|
129
|
+
const c = new index_1.SynapseOneClient({ baseUrl: base });
|
|
130
|
+
await c.getExecution('e1', true);
|
|
131
|
+
strict_1.default.equal(lastPath, '/v1/executions/e1?includeData=true');
|
|
132
|
+
});
|
|
133
|
+
(0, node_test_1.it)('iam usageSummary and usageEvents hit the right endpoints', async () => {
|
|
134
|
+
const c = new index_1.SynapseOneClient({ baseUrl: base });
|
|
135
|
+
await c.iam.usageSummary({ feature: 'workflows' });
|
|
136
|
+
strict_1.default.equal(lastPath, '/v1/usage/summary?feature=workflows');
|
|
137
|
+
strict_1.default.equal(lastMethod, 'GET');
|
|
138
|
+
await c.iam.usageEvents({ limit: 25, offset: 50, q: 'test' });
|
|
139
|
+
strict_1.default.ok(lastPath.startsWith('/v1/usage/events?'));
|
|
140
|
+
strict_1.default.ok(lastPath.includes('limit=25'));
|
|
141
|
+
strict_1.default.ok(lastPath.includes('offset=50'));
|
|
142
|
+
strict_1.default.ok(lastPath.includes('q=test'));
|
|
143
|
+
strict_1.default.equal(lastMethod, 'GET');
|
|
144
|
+
});
|
|
145
|
+
(0, node_test_1.it)('unwrapExecutions accepts envelope and bare array', () => {
|
|
146
|
+
strict_1.default.deepEqual((0, index_1.unwrapExecutions)({ data: [{ id: 'e1' }] }), [{ id: 'e1' }]);
|
|
147
|
+
strict_1.default.deepEqual((0, index_1.unwrapExecutions)([{ id: 'e2' }]), [{ id: 'e2' }]);
|
|
148
|
+
strict_1.default.deepEqual((0, index_1.unwrapExecutions)(null), []);
|
|
149
|
+
});
|
|
150
|
+
(0, node_test_1.it)('triggerWebhook posts to /webhook/:path without auth', async () => {
|
|
151
|
+
const out = await (0, index_1.triggerWebhook)(base, 'transform', { value: 21 });
|
|
152
|
+
strict_1.default.equal(lastPath, '/webhook/transform');
|
|
153
|
+
strict_1.default.equal(lastMethod, 'POST');
|
|
154
|
+
strict_1.default.deepEqual(JSON.parse(lastBody), { value: 21 });
|
|
155
|
+
strict_1.default.equal(out.ok, true);
|
|
156
|
+
});
|
|
157
|
+
(0, node_test_1.it)('call() passes any method/path/body through', async () => {
|
|
158
|
+
const c = new index_1.SynapseOneClient({ baseUrl: base });
|
|
159
|
+
await c.call('POST', '/v1/accounts/a1/credentials', { name: 'x' });
|
|
160
|
+
strict_1.default.equal(lastPath, '/v1/accounts/a1/credentials');
|
|
161
|
+
strict_1.default.equal(lastMethod, 'POST');
|
|
162
|
+
strict_1.default.deepEqual(JSON.parse(lastBody), { name: 'x' });
|
|
163
|
+
await c.call('get', 'v1/workflows');
|
|
164
|
+
strict_1.default.equal(lastPath, '/v1/workflows');
|
|
165
|
+
strict_1.default.equal(lastMethod, 'GET');
|
|
166
|
+
});
|
|
167
|
+
(0, node_test_1.it)('credentials CRUD hits the workflow credentials paths', async () => {
|
|
168
|
+
const c = new index_1.SynapseOneClient({ baseUrl: base });
|
|
169
|
+
await c.createCredential('a1', { name: 'stripe-live', type: 'bearer', secret: 's3cr3t', expiresAt: '2027-01-01T00:00:00Z' });
|
|
170
|
+
strict_1.default.equal(lastPath, '/v1/credentials');
|
|
171
|
+
strict_1.default.equal(lastMethod, 'POST');
|
|
172
|
+
strict_1.default.deepEqual(JSON.parse(lastBody), { accountId: 'a1', name: 'stripe-live', kind: 'bearer', secret: 's3cr3t' });
|
|
173
|
+
await c.listCredentials('a1');
|
|
174
|
+
strict_1.default.equal(lastPath, '/v1/credentials?accountId=a1');
|
|
175
|
+
strict_1.default.equal(lastMethod, 'GET');
|
|
176
|
+
await c.getCredential('c1');
|
|
177
|
+
strict_1.default.equal(lastPath, '/v1/credentials/c1');
|
|
178
|
+
await c.rotateCredential('c1', 'n3w');
|
|
179
|
+
strict_1.default.equal(lastPath, '/v1/credentials/c1/rotate');
|
|
180
|
+
strict_1.default.equal(lastMethod, 'POST');
|
|
181
|
+
strict_1.default.deepEqual(JSON.parse(lastBody), { secret: 'n3w' });
|
|
182
|
+
await c.deleteCredential('c1');
|
|
183
|
+
strict_1.default.equal(lastPath, '/v1/credentials/c1');
|
|
184
|
+
strict_1.default.equal(lastMethod, 'DELETE');
|
|
185
|
+
});
|
|
186
|
+
(0, node_test_1.it)('ai agent posts question and history', async () => {
|
|
187
|
+
const c = new index_1.SynapseOneClient({ baseUrl: base });
|
|
188
|
+
await c.ai.agent('how to create a workflow', [{ role: 'user', text: 'hi' }]);
|
|
189
|
+
strict_1.default.equal(lastPath, '/v1/ai/agent');
|
|
190
|
+
strict_1.default.equal(lastMethod, 'POST');
|
|
191
|
+
strict_1.default.deepEqual(JSON.parse(lastBody), {
|
|
192
|
+
question: 'how to create a workflow',
|
|
193
|
+
history: [{ role: 'user', text: 'hi' }],
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
(0, node_test_1.it)('ldsLicenses lists the license catalog', async () => {
|
|
197
|
+
const c = new index_1.SynapseOneClient({ baseUrl: base });
|
|
198
|
+
await c.ldsLicenses();
|
|
199
|
+
strict_1.default.equal(lastPath, '/v1/lds/licenses');
|
|
200
|
+
strict_1.default.equal(lastMethod, 'GET');
|
|
201
|
+
});
|
|
202
|
+
(0, node_test_1.it)('ldsDatasources passes orgId/accountId filters', async () => {
|
|
203
|
+
const c = new index_1.SynapseOneClient({ baseUrl: base });
|
|
204
|
+
await c.ldsDatasources({ orgId: 'o1', accountId: 'a1' });
|
|
205
|
+
strict_1.default.equal(lastPath, '/v1/lds/datasources?orgId=o1&accountId=a1');
|
|
206
|
+
await c.ldsDatasources();
|
|
207
|
+
strict_1.default.equal(lastPath, '/v1/lds/datasources');
|
|
208
|
+
});
|
|
209
|
+
(0, node_test_1.it)('ldsCreateDatasource posts the contract body', async () => {
|
|
210
|
+
const c = new index_1.SynapseOneClient({ baseUrl: base });
|
|
211
|
+
const input = {
|
|
212
|
+
name: 'Weather', slug: 'weather', entityType: 'WeatherObserved', license: 'CC-BY-4.0',
|
|
213
|
+
licenseUrl: 'https://creativecommons.org/licenses/by/4.0/',
|
|
214
|
+
contexts: ['https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld'],
|
|
215
|
+
defaultVisibility: 'public', description: 'demo datasource', orgId: 'o1', accountId: 'a1',
|
|
216
|
+
};
|
|
217
|
+
await c.ldsCreateDatasource(input);
|
|
218
|
+
strict_1.default.equal(lastPath, '/v1/lds/datasources');
|
|
219
|
+
strict_1.default.equal(lastMethod, 'POST');
|
|
220
|
+
strict_1.default.deepEqual(JSON.parse(lastBody), input);
|
|
221
|
+
});
|
|
222
|
+
(0, node_test_1.it)('covers LDS datasource/attribute CRUD paths', async () => {
|
|
223
|
+
const c = new index_1.SynapseOneClient({ baseUrl: base });
|
|
224
|
+
await c.ldsDatasource('ds1');
|
|
225
|
+
strict_1.default.equal(lastPath, '/v1/lds/datasources/ds1');
|
|
226
|
+
await c.ldsUpdateDatasource('ds1', { name: 'renamed' });
|
|
227
|
+
strict_1.default.equal(lastPath, '/v1/lds/datasources/ds1');
|
|
228
|
+
strict_1.default.equal(lastMethod, 'PATCH');
|
|
229
|
+
strict_1.default.deepEqual(JSON.parse(lastBody), { name: 'renamed' });
|
|
230
|
+
await c.ldsDeleteDatasource('ds1');
|
|
231
|
+
strict_1.default.equal(lastPath, '/v1/lds/datasources/ds1');
|
|
232
|
+
strict_1.default.equal(lastMethod, 'DELETE');
|
|
233
|
+
await c.ldsAttributes('ds1');
|
|
234
|
+
strict_1.default.equal(lastPath, '/v1/lds/datasources/ds1/attributes');
|
|
235
|
+
await c.ldsSetAttributes('ds1', [{ name: 'temperature', ngsiLdType: 'Property', visibility: 'public' }]);
|
|
236
|
+
strict_1.default.equal(lastPath, '/v1/lds/datasources/ds1/attributes');
|
|
237
|
+
strict_1.default.equal(lastMethod, 'PUT');
|
|
238
|
+
strict_1.default.deepEqual(JSON.parse(lastBody), { attributes: [{ name: 'temperature', ngsiLdType: 'Property', visibility: 'public' }] });
|
|
239
|
+
await c.ldsStats('ds1');
|
|
240
|
+
strict_1.default.equal(lastPath, '/v1/lds/datasources/ds1/stats');
|
|
241
|
+
await c.ldsEntity('ds1', 'urn:ngsi-ld:Thing:t1');
|
|
242
|
+
strict_1.default.equal(lastPath, '/v1/lds/datasources/ds1/entities/urn%3Angsi-ld%3AThing%3At1');
|
|
243
|
+
});
|
|
244
|
+
(0, node_test_1.it)('ldsIngest normalizes single/array/wrapped record bodies', async () => {
|
|
245
|
+
const c = new index_1.SynapseOneClient({ baseUrl: base });
|
|
246
|
+
await c.ldsIngest('ds1', { sensor: 's1', temperature: 21 });
|
|
247
|
+
strict_1.default.equal(lastPath, '/v1/lds/datasources/ds1/records');
|
|
248
|
+
strict_1.default.equal(lastMethod, 'POST');
|
|
249
|
+
strict_1.default.deepEqual(JSON.parse(lastBody), { record: { sensor: 's1', temperature: 21 } });
|
|
250
|
+
await c.ldsIngest('ds1', [{ sensor: 's1' }, { sensor: 's2' }]);
|
|
251
|
+
strict_1.default.deepEqual(JSON.parse(lastBody), { records: [{ sensor: 's1' }, { sensor: 's2' }] });
|
|
252
|
+
await c.ldsIngest('ds1', { records: [{ sensor: 's3' }] });
|
|
253
|
+
strict_1.default.deepEqual(JSON.parse(lastBody), { records: [{ sensor: 's3' }] });
|
|
254
|
+
});
|
|
255
|
+
(0, node_test_1.it)('ldsEntities sends limit/offset/visibility query params', async () => {
|
|
256
|
+
const c = new index_1.SynapseOneClient({ baseUrl: base });
|
|
257
|
+
await c.ldsEntities('ds1', { limit: 50, offset: 10, visibility: 'public' });
|
|
258
|
+
strict_1.default.equal(lastPath, '/v1/lds/datasources/ds1/entities?limit=50&offset=10&visibility=public');
|
|
259
|
+
await c.ldsEntities('ds1');
|
|
260
|
+
strict_1.default.equal(lastPath, '/v1/lds/datasources/ds1/entities');
|
|
261
|
+
});
|
|
262
|
+
(0, node_test_1.it)('ldsSetAttributes surfaces the 400 reserved_attribute error', async () => {
|
|
263
|
+
let errMethod = '';
|
|
264
|
+
let errPath = '';
|
|
265
|
+
let errBody = '';
|
|
266
|
+
const errSrv = node_http_1.default.createServer((req, res) => {
|
|
267
|
+
errMethod = req.method || '';
|
|
268
|
+
errPath = req.url || '';
|
|
269
|
+
let body = '';
|
|
270
|
+
req.on('data', (ch) => (body += ch));
|
|
271
|
+
req.on('end', () => {
|
|
272
|
+
errBody = body;
|
|
273
|
+
res.statusCode = 400;
|
|
274
|
+
res.setHeader('Content-Type', 'application/json');
|
|
275
|
+
res.end(JSON.stringify({ error: 'reserved_attribute', message: 'attribute name "id" is reserved' }));
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
await new Promise((resolve) => errSrv.listen(0, '127.0.0.1', resolve));
|
|
279
|
+
const a = errSrv.address();
|
|
280
|
+
const errBase = `http://127.0.0.1:${typeof a === 'object' && a ? a.port : 0}`;
|
|
281
|
+
try {
|
|
282
|
+
const c = new index_1.SynapseOneClient({ baseUrl: errBase });
|
|
283
|
+
await strict_1.default.rejects(() => c.ldsSetAttributes('ds1', [{ name: 'id' }]), /synapseone 400 .*reserved_attribute/);
|
|
284
|
+
strict_1.default.equal(errMethod, 'PUT');
|
|
285
|
+
strict_1.default.equal(errPath, '/v1/lds/datasources/ds1/attributes');
|
|
286
|
+
strict_1.default.deepEqual(JSON.parse(errBody), { attributes: [{ name: 'id' }] });
|
|
287
|
+
}
|
|
288
|
+
finally {
|
|
289
|
+
await new Promise((resolve) => errSrv.close(() => resolve()));
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/example.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/*
|
|
4
|
+
* Copyright 2026 SynapseOne Contributors
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
*/
|
|
7
|
+
const index_1 = require("./index");
|
|
8
|
+
async function main() {
|
|
9
|
+
const c = new index_1.SynapseOneClient({ baseUrl: process.env.SYNAPSEONE_BASE_URL || 'http://localhost:8080' });
|
|
10
|
+
const login = await c.login('demo@acme.local');
|
|
11
|
+
console.log('login ok:', login.user?.email);
|
|
12
|
+
c.setToken(login.accessToken);
|
|
13
|
+
console.log(await c.listOrgs());
|
|
14
|
+
console.log('lds datasources:', await c.ldsDatasources());
|
|
15
|
+
}
|
|
16
|
+
main().catch((e) => { console.error(e); process.exit(1); });
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { HttpClient } from './_http';
|
|
2
|
+
export declare class FaststoreService {
|
|
3
|
+
private readonly http;
|
|
4
|
+
constructor(http: HttpClient);
|
|
5
|
+
private key;
|
|
6
|
+
stats(): Promise<unknown>;
|
|
7
|
+
/** Scan keys; returns `{keys, cursor, truncated}` (pass `cursor` back to continue). */
|
|
8
|
+
listKeys(query?: {
|
|
9
|
+
prefix?: string;
|
|
10
|
+
limit?: number;
|
|
11
|
+
cursor?: string;
|
|
12
|
+
}): Promise<{
|
|
13
|
+
keys: any[];
|
|
14
|
+
cursor: string | null;
|
|
15
|
+
truncated: boolean;
|
|
16
|
+
}>;
|
|
17
|
+
exists(key: string): Promise<boolean>;
|
|
18
|
+
get(key: string): Promise<unknown>;
|
|
19
|
+
set(key: string, value: unknown, opts?: {
|
|
20
|
+
ttlSeconds?: number;
|
|
21
|
+
ifNotExists?: boolean;
|
|
22
|
+
ifExists?: boolean;
|
|
23
|
+
}): Promise<unknown>;
|
|
24
|
+
delete(key: string): Promise<unknown>;
|
|
25
|
+
ttl(key: string): Promise<unknown>;
|
|
26
|
+
expire(key: string, ttlSeconds: number): Promise<unknown>;
|
|
27
|
+
incr(key: string, opts?: {
|
|
28
|
+
by?: number;
|
|
29
|
+
ttlSeconds?: number;
|
|
30
|
+
}): Promise<unknown>;
|
|
31
|
+
batch(operations: Record<string, unknown>[]): Promise<{
|
|
32
|
+
results: any[];
|
|
33
|
+
ok: boolean;
|
|
34
|
+
}>;
|
|
35
|
+
hashGet(key: string): Promise<unknown>;
|
|
36
|
+
hashSet(key: string, values: Record<string, unknown>, opts?: {
|
|
37
|
+
ttlSeconds?: number;
|
|
38
|
+
}): Promise<unknown>;
|
|
39
|
+
hashDelete(key: string): Promise<unknown>;
|
|
40
|
+
hashLength(key: string): Promise<unknown>;
|
|
41
|
+
hashGetField(key: string, field: string): Promise<unknown>;
|
|
42
|
+
hashSetField(key: string, field: string, value: unknown, opts?: {
|
|
43
|
+
ttlSeconds?: number;
|
|
44
|
+
}): Promise<unknown>;
|
|
45
|
+
hashDeleteField(key: string, field: string): Promise<unknown>;
|
|
46
|
+
hashIncr(key: string, field: string, opts?: {
|
|
47
|
+
by?: number;
|
|
48
|
+
ttlSeconds?: number;
|
|
49
|
+
}): Promise<unknown>;
|
|
50
|
+
listRange(key: string, start?: number, stop?: number): Promise<unknown>;
|
|
51
|
+
listDelete(key: string): Promise<unknown>;
|
|
52
|
+
listPush(key: string, values: unknown[], opts?: {
|
|
53
|
+
side?: 'left' | 'right';
|
|
54
|
+
ttlSeconds?: number;
|
|
55
|
+
}): Promise<unknown>;
|
|
56
|
+
listPop(key: string, opts?: {
|
|
57
|
+
side?: 'left' | 'right';
|
|
58
|
+
count?: number;
|
|
59
|
+
}): Promise<unknown>;
|
|
60
|
+
listTrim(key: string, start: number, stop: number): Promise<unknown>;
|
|
61
|
+
listLength(key: string): Promise<unknown>;
|
|
62
|
+
setMembers(key: string): Promise<unknown>;
|
|
63
|
+
setDelete(key: string): Promise<unknown>;
|
|
64
|
+
setAdd(key: string, members: unknown[], opts?: {
|
|
65
|
+
ttlSeconds?: number;
|
|
66
|
+
}): Promise<unknown>;
|
|
67
|
+
setRemove(key: string, members: unknown[]): Promise<unknown>;
|
|
68
|
+
setContains(key: string, member: unknown): Promise<unknown>;
|
|
69
|
+
setCard(key: string): Promise<unknown>;
|
|
70
|
+
zsetRange(key: string, opts?: {
|
|
71
|
+
start?: number;
|
|
72
|
+
stop?: number;
|
|
73
|
+
rev?: boolean;
|
|
74
|
+
withScores?: boolean;
|
|
75
|
+
}): Promise<unknown>;
|
|
76
|
+
zsetDelete(key: string): Promise<unknown>;
|
|
77
|
+
zsetAdd(key: string, members: Array<{
|
|
78
|
+
member: unknown;
|
|
79
|
+
score: number;
|
|
80
|
+
}>, opts?: {
|
|
81
|
+
ttlSeconds?: number;
|
|
82
|
+
}): Promise<unknown>;
|
|
83
|
+
zsetIncr(key: string, member: unknown, opts?: {
|
|
84
|
+
by?: number;
|
|
85
|
+
ttlSeconds?: number;
|
|
86
|
+
}): Promise<unknown>;
|
|
87
|
+
zsetRemove(key: string, members: unknown[]): Promise<unknown>;
|
|
88
|
+
zsetScore(key: string, member: unknown): Promise<unknown>;
|
|
89
|
+
zsetRank(key: string, member: unknown, opts?: {
|
|
90
|
+
rev?: boolean;
|
|
91
|
+
}): Promise<unknown>;
|
|
92
|
+
zsetCard(key: string): Promise<unknown>;
|
|
93
|
+
lockAcquire(name: string, opts?: {
|
|
94
|
+
ttlSeconds?: number;
|
|
95
|
+
waitMs?: number;
|
|
96
|
+
token?: string;
|
|
97
|
+
}): Promise<{
|
|
98
|
+
acquired: boolean;
|
|
99
|
+
lock: string;
|
|
100
|
+
token?: string;
|
|
101
|
+
ttlSeconds?: number;
|
|
102
|
+
expiresAt?: string;
|
|
103
|
+
}>;
|
|
104
|
+
lockRelease(name: string, token: string): Promise<unknown>;
|
|
105
|
+
lockExtend(name: string, token: string, opts?: {
|
|
106
|
+
ttlSeconds?: number;
|
|
107
|
+
}): Promise<unknown>;
|
|
108
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright 2026 SynapseOne Contributors
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* Synapse Faststore client: Redis-backed cache/KV with strings, hashes, lists,
|
|
6
|
+
* sets, sorted sets, TTLs, counters, scans, batches and distributed locks.
|
|
7
|
+
*
|
|
8
|
+
* Every operation is scoped to the caller's tenant (org / account / user).
|
|
9
|
+
* Platform callers set `tenant` on the client (x-synapseone-tenant).
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.FaststoreService = void 0;
|
|
13
|
+
const _http_1 = require("./_http");
|
|
14
|
+
const P = '/v1/faststore';
|
|
15
|
+
class FaststoreService {
|
|
16
|
+
http;
|
|
17
|
+
constructor(http) {
|
|
18
|
+
this.http = http;
|
|
19
|
+
}
|
|
20
|
+
key(key) {
|
|
21
|
+
return encodeURIComponent(key);
|
|
22
|
+
}
|
|
23
|
+
// ---- stats / scans ----
|
|
24
|
+
stats() { return this.http.req(`${P}/stats`); }
|
|
25
|
+
/** Scan keys; returns `{keys, cursor, truncated}` (pass `cursor` back to continue). */
|
|
26
|
+
listKeys(query = {}) {
|
|
27
|
+
const p = new URLSearchParams();
|
|
28
|
+
if (query.prefix)
|
|
29
|
+
p.set('prefix', query.prefix);
|
|
30
|
+
if (query.limit !== undefined)
|
|
31
|
+
p.set('limit', String(query.limit));
|
|
32
|
+
if (query.cursor !== undefined)
|
|
33
|
+
p.set('cursor', query.cursor);
|
|
34
|
+
const s = p.toString();
|
|
35
|
+
return this.http.req(`${P}/keys${s ? `?${s}` : ''}`);
|
|
36
|
+
}
|
|
37
|
+
async exists(key) {
|
|
38
|
+
try {
|
|
39
|
+
await this.get(key);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
if (e instanceof _http_1.SynapseOneError && e.status === 404)
|
|
44
|
+
return false;
|
|
45
|
+
throw e;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// ---- key/value ----
|
|
49
|
+
get(key) { return this.http.req(`${P}/keys/${this.key(key)}`); }
|
|
50
|
+
set(key, value, opts = {}) {
|
|
51
|
+
return this.http.req(`${P}/keys/${this.key(key)}`, {
|
|
52
|
+
method: 'PUT',
|
|
53
|
+
body: JSON.stringify({
|
|
54
|
+
value,
|
|
55
|
+
...(opts.ttlSeconds !== undefined ? { ttlSeconds: opts.ttlSeconds } : {}),
|
|
56
|
+
...(opts.ifNotExists ? { ifNotExists: true } : {}),
|
|
57
|
+
...(opts.ifExists ? { ifExists: true } : {}),
|
|
58
|
+
}),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
delete(key) { return this.http.req(`${P}/keys/${this.key(key)}`, { method: 'DELETE' }); }
|
|
62
|
+
ttl(key) { return this.http.req(`${P}/keys/${this.key(key)}/ttl`); }
|
|
63
|
+
expire(key, ttlSeconds) {
|
|
64
|
+
return this.http.req(`${P}/keys/${this.key(key)}/expire`, { method: 'POST', body: JSON.stringify({ ttlSeconds }) });
|
|
65
|
+
}
|
|
66
|
+
incr(key, opts = {}) {
|
|
67
|
+
return this.http.req(`${P}/keys/${this.key(key)}/incr`, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
body: JSON.stringify({
|
|
70
|
+
by: opts.by ?? 1,
|
|
71
|
+
...(opts.ttlSeconds !== undefined ? { ttlSeconds: opts.ttlSeconds } : {}),
|
|
72
|
+
}),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
batch(operations) {
|
|
76
|
+
return this.http.req(`${P}/batch`, { method: 'POST', body: JSON.stringify({ operations }) });
|
|
77
|
+
}
|
|
78
|
+
// ---- hashes ----
|
|
79
|
+
hashGet(key) { return this.http.req(`${P}/hashes/${this.key(key)}`); }
|
|
80
|
+
hashSet(key, values, opts = {}) {
|
|
81
|
+
return this.http.req(`${P}/hashes/${this.key(key)}`, {
|
|
82
|
+
method: 'PUT',
|
|
83
|
+
body: JSON.stringify({ values, ...(opts.ttlSeconds !== undefined ? { ttlSeconds: opts.ttlSeconds } : {}) }),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
hashDelete(key) { return this.http.req(`${P}/hashes/${this.key(key)}`, { method: 'DELETE' }); }
|
|
87
|
+
hashLength(key) { return this.http.req(`${P}/hashes/${this.key(key)}/length`); }
|
|
88
|
+
hashGetField(key, field) {
|
|
89
|
+
return this.http.req(`${P}/hashes/${this.key(key)}/${encodeURIComponent(field)}`);
|
|
90
|
+
}
|
|
91
|
+
hashSetField(key, field, value, opts = {}) {
|
|
92
|
+
return this.http.req(`${P}/hashes/${this.key(key)}/${encodeURIComponent(field)}`, {
|
|
93
|
+
method: 'PUT',
|
|
94
|
+
body: JSON.stringify({ value, ...(opts.ttlSeconds !== undefined ? { ttlSeconds: opts.ttlSeconds } : {}) }),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
hashDeleteField(key, field) {
|
|
98
|
+
return this.http.req(`${P}/hashes/${this.key(key)}/${encodeURIComponent(field)}`, { method: 'DELETE' });
|
|
99
|
+
}
|
|
100
|
+
hashIncr(key, field, opts = {}) {
|
|
101
|
+
return this.http.req(`${P}/hashes/${this.key(key)}/incr`, {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
body: JSON.stringify({ field, by: opts.by ?? 1, ...(opts.ttlSeconds !== undefined ? { ttlSeconds: opts.ttlSeconds } : {}) }),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
// ---- lists ----
|
|
107
|
+
listRange(key, start = 0, stop = -1) {
|
|
108
|
+
return this.http.req(`${P}/lists/${this.key(key)}?start=${start}&stop=${stop}`);
|
|
109
|
+
}
|
|
110
|
+
listDelete(key) { return this.http.req(`${P}/lists/${this.key(key)}`, { method: 'DELETE' }); }
|
|
111
|
+
listPush(key, values, opts = {}) {
|
|
112
|
+
return this.http.req(`${P}/lists/${this.key(key)}/push`, {
|
|
113
|
+
method: 'POST',
|
|
114
|
+
body: JSON.stringify({ values, side: opts.side ?? 'right', ...(opts.ttlSeconds !== undefined ? { ttlSeconds: opts.ttlSeconds } : {}) }),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
listPop(key, opts = {}) {
|
|
118
|
+
return this.http.req(`${P}/lists/${this.key(key)}/pop`, {
|
|
119
|
+
method: 'POST',
|
|
120
|
+
body: JSON.stringify({ side: opts.side ?? 'left', count: opts.count ?? 1 }),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
listTrim(key, start, stop) {
|
|
124
|
+
return this.http.req(`${P}/lists/${this.key(key)}/trim`, { method: 'POST', body: JSON.stringify({ start, stop }) });
|
|
125
|
+
}
|
|
126
|
+
listLength(key) { return this.http.req(`${P}/lists/${this.key(key)}/length`); }
|
|
127
|
+
// ---- sets ----
|
|
128
|
+
setMembers(key) { return this.http.req(`${P}/sets/${this.key(key)}`); }
|
|
129
|
+
setDelete(key) { return this.http.req(`${P}/sets/${this.key(key)}`, { method: 'DELETE' }); }
|
|
130
|
+
setAdd(key, members, opts = {}) {
|
|
131
|
+
return this.http.req(`${P}/sets/${this.key(key)}/add`, {
|
|
132
|
+
method: 'POST',
|
|
133
|
+
body: JSON.stringify({ members, ...(opts.ttlSeconds !== undefined ? { ttlSeconds: opts.ttlSeconds } : {}) }),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
setRemove(key, members) {
|
|
137
|
+
return this.http.req(`${P}/sets/${this.key(key)}/remove`, { method: 'POST', body: JSON.stringify({ members }) });
|
|
138
|
+
}
|
|
139
|
+
setContains(key, member) {
|
|
140
|
+
return this.http.req(`${P}/sets/${this.key(key)}/contains`, { method: 'POST', body: JSON.stringify({ member }) });
|
|
141
|
+
}
|
|
142
|
+
setCard(key) { return this.http.req(`${P}/sets/${this.key(key)}/card`); }
|
|
143
|
+
// ---- sorted sets ----
|
|
144
|
+
zsetRange(key, opts = {}) {
|
|
145
|
+
const p = new URLSearchParams();
|
|
146
|
+
p.set('start', String(opts.start ?? 0));
|
|
147
|
+
p.set('stop', String(opts.stop ?? -1));
|
|
148
|
+
if (opts.rev)
|
|
149
|
+
p.set('rev', 'true');
|
|
150
|
+
if (opts.withScores)
|
|
151
|
+
p.set('withScores', 'true');
|
|
152
|
+
return this.http.req(`${P}/zsets/${this.key(key)}?${p.toString()}`);
|
|
153
|
+
}
|
|
154
|
+
zsetDelete(key) { return this.http.req(`${P}/zsets/${this.key(key)}`, { method: 'DELETE' }); }
|
|
155
|
+
zsetAdd(key, members, opts = {}) {
|
|
156
|
+
return this.http.req(`${P}/zsets/${this.key(key)}/add`, {
|
|
157
|
+
method: 'POST',
|
|
158
|
+
body: JSON.stringify({ members, ...(opts.ttlSeconds !== undefined ? { ttlSeconds: opts.ttlSeconds } : {}) }),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
zsetIncr(key, member, opts = {}) {
|
|
162
|
+
return this.http.req(`${P}/zsets/${this.key(key)}/incr`, {
|
|
163
|
+
method: 'POST',
|
|
164
|
+
body: JSON.stringify({ member, by: opts.by ?? 1, ...(opts.ttlSeconds !== undefined ? { ttlSeconds: opts.ttlSeconds } : {}) }),
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
zsetRemove(key, members) {
|
|
168
|
+
return this.http.req(`${P}/zsets/${this.key(key)}/remove`, { method: 'POST', body: JSON.stringify({ members }) });
|
|
169
|
+
}
|
|
170
|
+
zsetScore(key, member) {
|
|
171
|
+
return this.http.req(`${P}/zsets/${this.key(key)}/score`, { method: 'POST', body: JSON.stringify({ member }) });
|
|
172
|
+
}
|
|
173
|
+
zsetRank(key, member, opts = {}) {
|
|
174
|
+
return this.http.req(`${P}/zsets/${this.key(key)}/rank`, {
|
|
175
|
+
method: 'POST',
|
|
176
|
+
body: JSON.stringify({ member, ...(opts.rev ? { rev: true } : {}) }),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
zsetCard(key) { return this.http.req(`${P}/zsets/${this.key(key)}/card`); }
|
|
180
|
+
// ---- distributed locks ----
|
|
181
|
+
lockAcquire(name, opts = {}) {
|
|
182
|
+
return this.http.req(`${P}/locks/${encodeURIComponent(name)}/acquire`, {
|
|
183
|
+
method: 'POST',
|
|
184
|
+
body: JSON.stringify({
|
|
185
|
+
waitMs: opts.waitMs ?? 0,
|
|
186
|
+
...(opts.ttlSeconds !== undefined ? { ttlSeconds: opts.ttlSeconds } : {}),
|
|
187
|
+
...(opts.token !== undefined ? { token: opts.token } : {}),
|
|
188
|
+
}),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
lockRelease(name, token) {
|
|
192
|
+
return this.http.req(`${P}/locks/${encodeURIComponent(name)}/release`, { method: 'POST', body: JSON.stringify({ token }) });
|
|
193
|
+
}
|
|
194
|
+
lockExtend(name, token, opts = {}) {
|
|
195
|
+
return this.http.req(`${P}/locks/${encodeURIComponent(name)}/extend`, {
|
|
196
|
+
method: 'POST',
|
|
197
|
+
body: JSON.stringify({ token, ...(opts.ttlSeconds !== undefined ? { ttlSeconds: opts.ttlSeconds } : {}) }),
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
exports.FaststoreService = FaststoreService;
|