@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,99 @@
1
+ import { HttpClient } from './_http';
2
+ export interface StreamTopicConfig {
3
+ name: string;
4
+ maxLen?: number;
5
+ minId?: string;
6
+ retentionMs?: number;
7
+ createdAt: number;
8
+ updatedAt: number;
9
+ }
10
+ export interface StreamEvent<T = unknown> {
11
+ id: string;
12
+ payload: T;
13
+ key?: string;
14
+ headers?: Record<string, unknown>;
15
+ timestamp: number;
16
+ }
17
+ export interface StreamPublishInput {
18
+ payload: unknown;
19
+ key?: string;
20
+ headers?: Record<string, unknown>;
21
+ id?: string;
22
+ }
23
+ export interface StreamReadOptions {
24
+ fromId?: string;
25
+ count?: number;
26
+ blockMs?: number;
27
+ }
28
+ export interface StreamGroupReadOptions {
29
+ consumer?: string;
30
+ count?: number;
31
+ blockMs?: number;
32
+ autoAck?: boolean;
33
+ }
34
+ export interface StreamRangeOptions {
35
+ start?: string;
36
+ end?: string;
37
+ count?: number;
38
+ }
39
+ export declare class StreamService {
40
+ private readonly http;
41
+ constructor(http: HttpClient);
42
+ private t;
43
+ private g;
44
+ listTopics(): Promise<{
45
+ topics: StreamTopicConfig[];
46
+ }>;
47
+ createTopic(topic: string, opts?: {
48
+ maxLen?: number;
49
+ minId?: string;
50
+ retentionMs?: number;
51
+ }): Promise<{
52
+ topic: StreamTopicConfig;
53
+ }>;
54
+ getTopic(topic: string): Promise<{
55
+ topic: StreamTopicConfig;
56
+ }>;
57
+ deleteTopic(topic: string): Promise<{
58
+ deleted: boolean;
59
+ }>;
60
+ topicStats(topic: string): Promise<{
61
+ stats: any;
62
+ }>;
63
+ publish(topic: string, payload: unknown, opts?: {
64
+ key?: string;
65
+ headers?: Record<string, unknown>;
66
+ id?: string;
67
+ }): Promise<{
68
+ event: StreamEvent;
69
+ }>;
70
+ publishBatch(topic: string, events: StreamPublishInput[]): Promise<{
71
+ events: StreamEvent[];
72
+ count: number;
73
+ }>;
74
+ read(topic: string, opts?: StreamReadOptions): Promise<{
75
+ events: StreamEvent[];
76
+ count: number;
77
+ }>;
78
+ range(topic: string, opts?: StreamRangeOptions): Promise<{
79
+ events: StreamEvent[];
80
+ count: number;
81
+ }>;
82
+ createGroup(topic: string, group: string, opts?: {
83
+ startId?: string;
84
+ }): Promise<{
85
+ group: string;
86
+ topic: string;
87
+ startId: string;
88
+ }>;
89
+ destroyGroup(topic: string, group: string): Promise<{
90
+ destroyed: boolean;
91
+ }>;
92
+ readGroup(topic: string, group: string, opts?: StreamGroupReadOptions): Promise<{
93
+ events: StreamEvent[];
94
+ count: number;
95
+ }>;
96
+ ack(topic: string, group: string, ids: string[]): Promise<{
97
+ acked: number;
98
+ }>;
99
+ }
package/dist/stream.js ADDED
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright 2026 SynapseOne Contributors
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * SynapseStream client: topics, produce (single & batch), consumer groups,
6
+ * non-destructive reads, time-range queries and retention trimming.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.StreamService = void 0;
10
+ const P = '/v1/stream/topics';
11
+ class StreamService {
12
+ http;
13
+ constructor(http) {
14
+ this.http = http;
15
+ }
16
+ t(topic) {
17
+ return encodeURIComponent(topic);
18
+ }
19
+ g(group) {
20
+ return encodeURIComponent(group);
21
+ }
22
+ // ---- topics ----
23
+ listTopics() {
24
+ return this.http.req(P);
25
+ }
26
+ createTopic(topic, opts = {}) {
27
+ return this.http.req(`${P}/${this.t(topic)}`, {
28
+ method: 'PUT',
29
+ body: JSON.stringify({
30
+ ...(opts.maxLen !== undefined ? { maxLen: opts.maxLen } : {}),
31
+ ...(opts.minId !== undefined ? { minId: opts.minId } : {}),
32
+ ...(opts.retentionMs !== undefined ? { retentionMs: opts.retentionMs } : {}),
33
+ }),
34
+ });
35
+ }
36
+ getTopic(topic) {
37
+ return this.http.req(`${P}/${this.t(topic)}`);
38
+ }
39
+ deleteTopic(topic) {
40
+ return this.http.req(`${P}/${this.t(topic)}`, { method: 'DELETE' });
41
+ }
42
+ topicStats(topic) {
43
+ return this.http.req(`${P}/${this.t(topic)}/stats`);
44
+ }
45
+ // ---- publish ----
46
+ publish(topic, payload, opts = {}) {
47
+ return this.http.req(`${P}/${this.t(topic)}/events`, {
48
+ method: 'POST',
49
+ body: JSON.stringify({
50
+ payload,
51
+ ...(opts.key !== undefined ? { key: opts.key } : {}),
52
+ ...(opts.headers !== undefined ? { headers: opts.headers } : {}),
53
+ ...(opts.id !== undefined ? { id: opts.id } : {}),
54
+ }),
55
+ });
56
+ }
57
+ publishBatch(topic, events) {
58
+ return this.http.req(`${P}/${this.t(topic)}/events:batch`, {
59
+ method: 'POST',
60
+ body: JSON.stringify({ events }),
61
+ });
62
+ }
63
+ // ---- read / range ----
64
+ read(topic, opts = {}) {
65
+ return this.http.req(`${P}/${this.t(topic)}/read`, {
66
+ method: 'POST',
67
+ body: JSON.stringify({
68
+ ...(opts.fromId !== undefined ? { fromId: opts.fromId } : {}),
69
+ ...(opts.count !== undefined ? { count: opts.count } : {}),
70
+ ...(opts.blockMs !== undefined ? { blockMs: opts.blockMs } : {}),
71
+ }),
72
+ });
73
+ }
74
+ range(topic, opts = {}) {
75
+ const qs = new URLSearchParams();
76
+ if (opts.start)
77
+ qs.set('start', opts.start);
78
+ if (opts.end)
79
+ qs.set('end', opts.end);
80
+ if (opts.count !== undefined)
81
+ qs.set('count', String(opts.count));
82
+ const s = qs.toString();
83
+ return this.http.req(`${P}/${this.t(topic)}/range${s ? `?${s}` : ''}`);
84
+ }
85
+ // ---- consumer groups ----
86
+ createGroup(topic, group, opts = {}) {
87
+ return this.http.req(`${P}/${this.t(topic)}/groups/${this.g(group)}`, {
88
+ method: 'PUT',
89
+ body: JSON.stringify({
90
+ ...(opts.startId !== undefined ? { startId: opts.startId } : {}),
91
+ }),
92
+ });
93
+ }
94
+ destroyGroup(topic, group) {
95
+ return this.http.req(`${P}/${this.t(topic)}/groups/${this.g(group)}`, {
96
+ method: 'DELETE',
97
+ });
98
+ }
99
+ readGroup(topic, group, opts = {}) {
100
+ return this.http.req(`${P}/${this.t(topic)}/groups/${this.g(group)}/read`, {
101
+ method: 'POST',
102
+ body: JSON.stringify({
103
+ consumer: opts.consumer || 'default-worker',
104
+ ...(opts.count !== undefined ? { count: opts.count } : {}),
105
+ ...(opts.blockMs !== undefined ? { blockMs: opts.blockMs } : {}),
106
+ ...(opts.autoAck !== undefined ? { autoAck: opts.autoAck } : {}),
107
+ }),
108
+ });
109
+ }
110
+ ack(topic, group, ids) {
111
+ return this.http.req(`${P}/${this.t(topic)}/groups/${this.g(group)}/ack`, {
112
+ method: 'POST',
113
+ body: JSON.stringify({ ids }),
114
+ });
115
+ }
116
+ }
117
+ exports.StreamService = StreamService;
@@ -0,0 +1,82 @@
1
+ import { HttpClient } from './_http';
2
+ /** Credential kinds accepted by the workflow engine (used by http/webhook nodes). */
3
+ export type CredentialKind = 'bearer' | 'api-key' | 'basic' | 'hmac';
4
+ export interface CredentialInput {
5
+ /** Owning account (credentials are account-scoped, org-bound for org callers). */
6
+ accountId: string;
7
+ name: string;
8
+ kind?: CredentialKind | string;
9
+ type?: CredentialKind | string;
10
+ /** Write-only: never returned by any endpoint. */
11
+ secret: string;
12
+ }
13
+ export interface CredentialMetadata {
14
+ id: string;
15
+ accountId: string;
16
+ name: string;
17
+ kind: CredentialKind | string;
18
+ createdAt: string;
19
+ updatedAt: string;
20
+ }
21
+ export declare class WorkflowService {
22
+ private readonly http;
23
+ constructor(http: HttpClient);
24
+ listTemplates(): Promise<unknown>;
25
+ getTemplate(name: string): Promise<unknown>;
26
+ listWorkflows(): Promise<unknown>;
27
+ getWorkflow(id: string): Promise<unknown>;
28
+ createWorkflowFromTemplate(template: string, name?: string): Promise<unknown>;
29
+ createWorkflow(name: string, definition: any, settings?: Record<string, unknown>): Promise<unknown>;
30
+ updateWorkflow(id: string, patch: {
31
+ name?: string;
32
+ definition?: any;
33
+ settings?: Record<string, unknown>;
34
+ }): Promise<unknown>;
35
+ deleteWorkflow(id: string): Promise<unknown>;
36
+ activateWorkflow(id: string): Promise<unknown>;
37
+ deactivateWorkflow(id: string): Promise<unknown>;
38
+ executeWorkflow(id: string, payload?: Record<string, unknown>): Promise<unknown>;
39
+ getExecution(id: string, includeData?: boolean): Promise<unknown>;
40
+ listExecutions(query?: string | Record<string, string | number | boolean | undefined>): Promise<any[]>;
41
+ testRun(definition: any, input?: Record<string, unknown>, workflowId?: string): Promise<unknown>;
42
+ createCredential(inputOrAccountId: CredentialInput | string, opts?: {
43
+ name: string;
44
+ kind?: CredentialKind | string;
45
+ type?: CredentialKind | string;
46
+ secret: string;
47
+ expiresAt?: string;
48
+ }): Promise<CredentialMetadata>;
49
+ listCredentials(accountId?: string): Promise<CredentialMetadata[]>;
50
+ getCredential(id: string): Promise<CredentialMetadata>;
51
+ rotateCredential(id: string, secret: string): Promise<CredentialMetadata>;
52
+ deleteCredential(id: string): Promise<unknown>;
53
+ /**
54
+ * Fire a public inbound webhook through the gateway: `ANY /webhook/:path`.
55
+ * No auth is required; per-workflow auth/rate-limit/idempotency still apply.
56
+ * `path` is the webhook node's `parameters.path`, not a workflow id.
57
+ */
58
+ triggerWebhook(path: string, payload?: unknown, init?: {
59
+ method?: string;
60
+ }): Promise<unknown>;
61
+ }
62
+ /** Normalize GET /v1/credentials: the API returns `{data: []}`. */
63
+ export declare function unwrapCredentialList(payload: any): CredentialMetadata[];
64
+ /**
65
+ * Normalize GET /v1/executions responses: the engine returns `{data: []}`
66
+ * (envelope, forward-compat with the old n8n proxy); older mocks return a
67
+ * bare array. Accepts both so callers always get an array.
68
+ */
69
+ export declare function unwrapExecutions(payload: any): any[];
70
+ /**
71
+ * Fire a public inbound webhook: `ANY /webhook/:path` (no auth) triggers the
72
+ * ACTIVE workflow whose webhook node declares that `path`.
73
+ *
74
+ * The gateway proxies `/webhook/*` to the workflow service, so `webhookBaseUrl`
75
+ * is normally the gateway URL. Use `client.workflow.triggerWebhook()` for the
76
+ * gateway-bound convenience form. `path` is the webhook node's
77
+ * `parameters.path` (e.g. `synapseone-event`), not a workflow id.
78
+ */
79
+ export declare function triggerWebhook(webhookBaseUrl: string, path: string, payload?: unknown, init?: {
80
+ method?: string;
81
+ timeoutMs?: number;
82
+ }): Promise<any>;
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright 2026 SynapseOne Contributors
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * Workflow client: templates, workflow CRUD/lifecycle, executions, test runs
6
+ * and public inbound webhooks.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.WorkflowService = void 0;
10
+ exports.unwrapCredentialList = unwrapCredentialList;
11
+ exports.unwrapExecutions = unwrapExecutions;
12
+ exports.triggerWebhook = triggerWebhook;
13
+ class WorkflowService {
14
+ http;
15
+ constructor(http) {
16
+ this.http = http;
17
+ }
18
+ // ---- templates ----
19
+ listTemplates() { return this.http.req('/v1/templates'); }
20
+ getTemplate(name) { return this.http.req(`/v1/templates/${encodeURIComponent(name)}`); }
21
+ // ---- workflows ----
22
+ listWorkflows() { return this.http.req('/v1/workflows'); }
23
+ getWorkflow(id) { return this.http.req(`/v1/workflows/${encodeURIComponent(id)}`); }
24
+ createWorkflowFromTemplate(template, name) {
25
+ return this.http.req('/v1/workflows', { method: 'POST', body: JSON.stringify({ template, name }) });
26
+ }
27
+ createWorkflow(name, definition, settings) {
28
+ return this.http.req('/v1/workflows', { method: 'POST', body: JSON.stringify({ name, definition, ...(settings ? { settings } : {}) }) });
29
+ }
30
+ updateWorkflow(id, patch) {
31
+ return this.http.req(`/v1/workflows/${encodeURIComponent(id)}`, { method: 'PUT', body: JSON.stringify(patch) });
32
+ }
33
+ deleteWorkflow(id) {
34
+ return this.http.req(`/v1/workflows/${encodeURIComponent(id)}`, { method: 'DELETE' });
35
+ }
36
+ activateWorkflow(id) { return this.http.req(`/v1/workflows/${encodeURIComponent(id)}/activate`, { method: 'POST' }); }
37
+ deactivateWorkflow(id) { return this.http.req(`/v1/workflows/${encodeURIComponent(id)}/deactivate`, { method: 'POST' }); }
38
+ executeWorkflow(id, payload = {}) {
39
+ return this.http.req(`/v1/workflows/${encodeURIComponent(id)}/execute`, { method: 'POST', body: JSON.stringify(payload) });
40
+ }
41
+ // ---- executions ----
42
+ getExecution(id, includeData = false) {
43
+ const qs = includeData ? '?includeData=true' : '';
44
+ return this.http.req(`/v1/executions/${encodeURIComponent(id)}${qs}`);
45
+ }
46
+ listExecutions(query = '') {
47
+ let q = '';
48
+ if (typeof query === 'string') {
49
+ q = query ? (query.startsWith('?') ? query : `?${query}`) : '';
50
+ }
51
+ else if (query && typeof query === 'object') {
52
+ // Allowlist mirrors GET /v1/executions (limit/offset/workflowId/status/triggeredBy/includeData).
53
+ const allowed = ['limit', 'offset', 'workflowId', 'status', 'triggeredBy', 'includeData'];
54
+ const params = new URLSearchParams();
55
+ for (const k of allowed) {
56
+ const v = query[k];
57
+ if (v !== undefined && v !== null && String(v) !== '')
58
+ params.set(k, String(v));
59
+ }
60
+ const s = params.toString();
61
+ q = s ? `?${s}` : '';
62
+ }
63
+ return this.http.req(`/v1/executions${q}`).then(unwrapExecutions);
64
+ }
65
+ testRun(definition, input = {}, workflowId) {
66
+ return this.http.req('/v1/workflows/test-run', { method: 'POST', body: JSON.stringify({ definition, input, ...(workflowId ? { workflowId } : {}) }) });
67
+ }
68
+ // ---- credentials (account-scoped secrets, encrypted at rest) ----
69
+ // Secrets are write-only: create/rotate accept `secret` once, every read
70
+ // returns metadata only. Kinds: bearer | api-key | basic | hmac.
71
+ createCredential(inputOrAccountId, opts) {
72
+ const body = typeof inputOrAccountId === 'string'
73
+ ? {
74
+ accountId: inputOrAccountId,
75
+ name: opts?.name,
76
+ kind: opts?.kind || opts?.type || 'bearer',
77
+ secret: opts?.secret,
78
+ }
79
+ : {
80
+ accountId: inputOrAccountId.accountId,
81
+ name: inputOrAccountId.name,
82
+ kind: inputOrAccountId.kind || inputOrAccountId.type || 'bearer',
83
+ secret: inputOrAccountId.secret,
84
+ };
85
+ return this.http.req('/v1/credentials', {
86
+ method: 'POST',
87
+ body: JSON.stringify(body),
88
+ });
89
+ }
90
+ listCredentials(accountId) {
91
+ const qs = accountId ? `?accountId=${encodeURIComponent(accountId)}` : '';
92
+ return this.http.req(`/v1/credentials${qs}`).then(unwrapCredentialList);
93
+ }
94
+ getCredential(id) {
95
+ return this.http.req(`/v1/credentials/${encodeURIComponent(id)}`);
96
+ }
97
+ rotateCredential(id, secret) {
98
+ return this.http.req(`/v1/credentials/${encodeURIComponent(id)}/rotate`, {
99
+ method: 'POST',
100
+ body: JSON.stringify({ secret }),
101
+ });
102
+ }
103
+ deleteCredential(id) {
104
+ return this.http.req(`/v1/credentials/${encodeURIComponent(id)}`, { method: 'DELETE' });
105
+ }
106
+ // ---- public inbound webhooks ----
107
+ /**
108
+ * Fire a public inbound webhook through the gateway: `ANY /webhook/:path`.
109
+ * No auth is required; per-workflow auth/rate-limit/idempotency still apply.
110
+ * `path` is the webhook node's `parameters.path`, not a workflow id.
111
+ */
112
+ triggerWebhook(path, payload, init = {}) {
113
+ const clean = String(path || '').replace(/^\/+|\/+$/g, '').split('?')[0];
114
+ if (!clean)
115
+ throw new Error('synapseone webhook: path required');
116
+ const method = (init.method || (payload === undefined ? 'GET' : 'POST')).toUpperCase();
117
+ return this.http.call(method, `/webhook/${clean}`, payload);
118
+ }
119
+ }
120
+ exports.WorkflowService = WorkflowService;
121
+ /** Normalize GET /v1/credentials: the API returns `{data: []}`. */
122
+ function unwrapCredentialList(payload) {
123
+ if (Array.isArray(payload))
124
+ return payload;
125
+ if (payload && Array.isArray(payload.data))
126
+ return payload.data;
127
+ return payload == null ? [] : payload;
128
+ }
129
+ /**
130
+ * Normalize GET /v1/executions responses: the engine returns `{data: []}`
131
+ * (envelope, forward-compat with the old n8n proxy); older mocks return a
132
+ * bare array. Accepts both so callers always get an array.
133
+ */
134
+ function unwrapExecutions(payload) {
135
+ if (Array.isArray(payload))
136
+ return payload;
137
+ if (payload && Array.isArray(payload.data))
138
+ return payload.data;
139
+ return payload == null ? [] : payload;
140
+ }
141
+ /**
142
+ * Fire a public inbound webhook: `ANY /webhook/:path` (no auth) triggers the
143
+ * ACTIVE workflow whose webhook node declares that `path`.
144
+ *
145
+ * The gateway proxies `/webhook/*` to the workflow service, so `webhookBaseUrl`
146
+ * is normally the gateway URL. Use `client.workflow.triggerWebhook()` for the
147
+ * gateway-bound convenience form. `path` is the webhook node's
148
+ * `parameters.path` (e.g. `synapseone-event`), not a workflow id.
149
+ */
150
+ async function triggerWebhook(webhookBaseUrl, path, payload, init) {
151
+ const base = webhookBaseUrl.replace(/\/$/, '');
152
+ const clean = String(path || '').replace(/^\/+|\/+$/g, '').split('?')[0];
153
+ if (!clean)
154
+ throw new Error('synapseone webhook: path required');
155
+ const method = (init?.method || (payload === undefined ? 'GET' : 'POST')).toUpperCase();
156
+ const url = `${base}/webhook/${clean}`;
157
+ const ctl = new AbortController();
158
+ const to = setTimeout(() => ctl.abort(), init?.timeoutMs ?? 15000);
159
+ let r;
160
+ try {
161
+ r = await fetch(url, {
162
+ method,
163
+ signal: ctl.signal,
164
+ headers: { 'Content-Type': 'application/json' },
165
+ body: payload === undefined ? undefined : JSON.stringify(payload),
166
+ });
167
+ }
168
+ catch (e) {
169
+ throw new Error(`synapseone network /webhook/${clean}: ${e?.message || e}`);
170
+ }
171
+ finally {
172
+ clearTimeout(to);
173
+ }
174
+ const text = await r.text();
175
+ let data = null;
176
+ try {
177
+ data = text ? JSON.parse(text) : null;
178
+ }
179
+ catch {
180
+ data = text;
181
+ }
182
+ if (!r.ok)
183
+ throw new Error(`synapseone ${r.status} /webhook/${clean}: ${typeof data === 'string' ? data : JSON.stringify(data)}`);
184
+ return data;
185
+ }
package/package.json CHANGED
@@ -1,6 +1,83 @@
1
1
  {
2
2
  "name": "@synapseone/sdk",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.7.0-dev.43",
4
+ "license": "Apache-2.0",
5
+ "description": "synapseone SDK for JavaScript/TypeScript",
6
+ "homepage": "https://synapseone.space",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/vku-open-source-2026/SynapseOne.git",
10
+ "directory": "sdks/sdk-js"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/vku-open-source-2026/SynapseOne/issues"
14
+ },
15
+ "keywords": [
16
+ "synapseone",
17
+ "sdk",
18
+ "paas",
19
+ "iam",
20
+ "workflow",
21
+ "ngsi-ld",
22
+ "document-db",
23
+ "redis",
24
+ "queue"
25
+ ],
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "main": "dist/index.js",
30
+ "types": "dist/index.d.ts",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "require": "./dist/index.js",
35
+ "default": "./dist/index.js"
36
+ },
37
+ "./iam": {
38
+ "types": "./dist/iam.d.ts",
39
+ "require": "./dist/iam.js",
40
+ "default": "./dist/iam.js"
41
+ },
42
+ "./workflow": {
43
+ "types": "./dist/workflow.d.ts",
44
+ "require": "./dist/workflow.js",
45
+ "default": "./dist/workflow.js"
46
+ },
47
+ "./lds": {
48
+ "types": "./dist/lds.d.ts",
49
+ "require": "./dist/lds.js",
50
+ "default": "./dist/lds.js"
51
+ },
52
+ "./base": {
53
+ "types": "./dist/base.d.ts",
54
+ "require": "./dist/base.js",
55
+ "default": "./dist/base.js"
56
+ },
57
+ "./faststore": {
58
+ "types": "./dist/faststore.d.ts",
59
+ "require": "./dist/faststore.js",
60
+ "default": "./dist/faststore.js"
61
+ },
62
+ "./queue": {
63
+ "types": "./dist/queue.d.ts",
64
+ "require": "./dist/queue.js",
65
+ "default": "./dist/queue.js"
66
+ },
67
+ "./package.json": "./package.json"
68
+ },
69
+ "files": [
70
+ "dist",
71
+ "README.md"
72
+ ],
73
+ "scripts": {
74
+ "build": "tsc -p tsconfig.json",
75
+ "test": "node --test dist/client.test.js dist/services.test.js",
76
+ "lint": "echo lint-ok"
77
+ },
78
+ "dependencies": {},
79
+ "devDependencies": {
80
+ "typescript": "^5.5.0",
81
+ "@types/node": "^20.14.0"
82
+ }
83
+ }