@synapseone/sdk 0.7.0 → 0.7.1-dev.56

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/dist/iam.d.ts CHANGED
@@ -1,12 +1,92 @@
1
- import { HttpClient } from './_http';
1
+ import { HttpClient, RequestOptions } from './_http';
2
+ export interface RegisterInput {
3
+ email: string;
4
+ password: string;
5
+ displayName?: string;
6
+ orgSlug?: string;
7
+ }
8
+ export interface PasswordLoginInput {
9
+ email: string;
10
+ password: string;
11
+ orgSlug?: string;
12
+ }
13
+ export interface Session {
14
+ id: string;
15
+ created_at: string | null;
16
+ expires_at: string | null;
17
+ revoked_at: string | null;
18
+ }
19
+ export interface ExchangeInput {
20
+ userId: string;
21
+ accountId?: string;
22
+ orgId?: string;
23
+ scopes?: string[];
24
+ audience?: string;
25
+ ttlSeconds?: number;
26
+ }
27
+ export interface DelegatedToken {
28
+ accessToken: string;
29
+ expiresIn: number;
30
+ tokenType: 'Bearer';
31
+ delegated: true;
32
+ userId: string;
33
+ orgId: string;
34
+ accountId: string;
35
+ scopes: string[];
36
+ audience?: string;
37
+ }
2
38
  export declare class IamService {
3
39
  private readonly http;
4
40
  constructor(http: HttpClient);
41
+ /** Passwordless dev login (IAM_ALLOW_DEV_LOGIN; disabled in production). */
5
42
  login(email: string, orgSlug?: string): Promise<{
6
43
  accessToken: string;
7
44
  refreshToken: string;
8
45
  user: any;
9
46
  }>;
47
+ /** First-party registration (email + password). */
48
+ register(input: RegisterInput): Promise<{
49
+ accessToken: string;
50
+ refreshToken: string;
51
+ user: any;
52
+ emailVerificationToken?: string;
53
+ }>;
54
+ /** First-party email+password login (works in production). */
55
+ loginWithPassword(input: PasswordLoginInput): Promise<{
56
+ accessToken: string;
57
+ refreshToken: string;
58
+ user: any;
59
+ }>;
60
+ changePassword(currentPassword: string, newPassword: string): Promise<{
61
+ ok: boolean;
62
+ revokedSessions: number;
63
+ }>;
64
+ /** Request a reset token. The response is always 202 (no account enumeration).
65
+ * In non-production, `resetToken` is returned when no mailer is configured. */
66
+ requestPasswordReset(email: string, orgSlug?: string): Promise<{
67
+ ok: boolean;
68
+ resetToken?: string;
69
+ }>;
70
+ resetPassword(token: string, newPassword: string): Promise<{
71
+ ok: boolean;
72
+ revokedSessions: number;
73
+ }>;
74
+ verifyEmail(token: string): Promise<{
75
+ ok: boolean;
76
+ emailVerified: boolean;
77
+ }>;
78
+ requestEmailVerification(): Promise<{
79
+ ok: boolean;
80
+ emailVerified?: boolean;
81
+ emailVerificationToken?: string;
82
+ }>;
83
+ listSessions(): Promise<Session[]>;
84
+ revokeSession(id: string): Promise<unknown>;
85
+ /**
86
+ * Delegated token exchange (Tier 2 → Tier 1). The client must present a
87
+ * service-account Bearer token or an API key; a user JWT is refused.
88
+ */
89
+ exchangeToken(input: ExchangeInput, opts?: RequestOptions): Promise<DelegatedToken>;
10
90
  refresh(refreshToken: string): Promise<{
11
91
  accessToken: string;
12
92
  refreshToken: string;
@@ -50,8 +130,28 @@ export declare class IamService {
50
130
  simulate(documents: any[], action: string, resource: string, context?: Record<string, unknown>): Promise<{
51
131
  allowed: boolean;
52
132
  }>;
53
- listAudit(limit?: number, actor?: string): Promise<unknown>;
54
- createApiKey(accountId: string, name?: string, scopes?: string[]): Promise<{
133
+ /**
134
+ * Read the tenant-scoped audit trail. Accepts the legacy `(limit, actor)`
135
+ * form or a filter object with action/resource/time/pagination (#32).
136
+ */
137
+ listAudit(limitOrFilter?: number | {
138
+ limit?: number;
139
+ offset?: number;
140
+ actor?: string;
141
+ allowed?: boolean;
142
+ action?: string;
143
+ resource?: string;
144
+ from?: string | Date;
145
+ to?: string | Date;
146
+ }, actor?: string): Promise<unknown>;
147
+ /**
148
+ * Create an API key. Scopes default to least-privilege read access
149
+ * (`*:read`); pass `['*']` explicitly for a full administrator key.
150
+ */
151
+ createApiKey(accountId: string, name?: string, scopes?: string[], opts?: {
152
+ userId?: string;
153
+ expiresInSeconds?: number;
154
+ }): Promise<{
55
155
  apiKey: string;
56
156
  }>;
57
157
  revokeApiKey(keyId: string): Promise<unknown>;
package/dist/iam.js CHANGED
@@ -1,4 +1,6 @@
1
1
  "use strict";
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ // SPDX-FileCopyrightText: 2026 VKU.VIC
2
4
  /*
3
5
  * Copyright 2026 SynapseOne Contributors
4
6
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,11 +18,64 @@ class IamService {
16
18
  this.http = http;
17
19
  }
18
20
  // ---- auth ----
21
+ /** Passwordless dev login (IAM_ALLOW_DEV_LOGIN; disabled in production). */
19
22
  login(email, orgSlug = 'acme') {
20
23
  return this.http.req('/v1/auth/login', {
21
24
  method: 'POST', body: JSON.stringify({ email, orgSlug }),
22
25
  });
23
26
  }
27
+ /** First-party registration (email + password). */
28
+ register(input) {
29
+ return this.http.req('/v1/auth/register', {
30
+ method: 'POST', body: JSON.stringify(input),
31
+ });
32
+ }
33
+ /** First-party email+password login (works in production). */
34
+ loginWithPassword(input) {
35
+ return this.http.req('/v1/auth/login', {
36
+ method: 'POST', body: JSON.stringify(input),
37
+ });
38
+ }
39
+ changePassword(currentPassword, newPassword) {
40
+ return this.http.req('/v1/auth/password/change', {
41
+ method: 'POST', body: JSON.stringify({ currentPassword, newPassword }),
42
+ });
43
+ }
44
+ /** Request a reset token. The response is always 202 (no account enumeration).
45
+ * In non-production, `resetToken` is returned when no mailer is configured. */
46
+ requestPasswordReset(email, orgSlug = 'acme') {
47
+ return this.http.req('/v1/auth/password/forgot', {
48
+ method: 'POST', body: JSON.stringify({ email, orgSlug }),
49
+ });
50
+ }
51
+ resetPassword(token, newPassword) {
52
+ return this.http.req('/v1/auth/password/reset', {
53
+ method: 'POST', body: JSON.stringify({ token, newPassword }),
54
+ });
55
+ }
56
+ verifyEmail(token) {
57
+ return this.http.req('/v1/auth/email/verify', {
58
+ method: 'POST', body: JSON.stringify({ token }),
59
+ });
60
+ }
61
+ requestEmailVerification() {
62
+ return this.http.req('/v1/auth/email/verify/request', {
63
+ method: 'POST', body: JSON.stringify({}),
64
+ });
65
+ }
66
+ listSessions() { return this.http.req('/v1/auth/sessions'); }
67
+ revokeSession(id) {
68
+ return this.http.req(`/v1/auth/sessions/${encodeURIComponent(id)}`, { method: 'DELETE' });
69
+ }
70
+ /**
71
+ * Delegated token exchange (Tier 2 → Tier 1). The client must present a
72
+ * service-account Bearer token or an API key; a user JWT is refused.
73
+ */
74
+ exchangeToken(input, opts = {}) {
75
+ return this.http.req('/v1/auth/exchange', {
76
+ method: 'POST', body: JSON.stringify(input),
77
+ }, opts);
78
+ }
24
79
  refresh(refreshToken) {
25
80
  return this.http.req('/v1/auth/refresh', {
26
81
  method: 'POST', body: JSON.stringify({ refreshToken }),
@@ -90,13 +145,30 @@ class IamService {
90
145
  simulate(documents, action, resource, context) {
91
146
  return this.http.req('/v1/authorize/dry-run', { method: 'POST', body: JSON.stringify({ documents, action, resource, context }) });
92
147
  }
93
- listAudit(limit = 50, actor) {
94
- const qs = `?limit=${limit}${actor ? `&actor=${encodeURIComponent(actor)}` : ''}`;
95
- return this.http.req(`/v1/audit${qs}`);
148
+ /**
149
+ * Read the tenant-scoped audit trail. Accepts the legacy `(limit, actor)`
150
+ * form or a filter object with action/resource/time/pagination (#32).
151
+ */
152
+ listAudit(limitOrFilter = 50, actor) {
153
+ const f = typeof limitOrFilter === 'number' ? { limit: limitOrFilter, actor } : limitOrFilter;
154
+ const qs = new URLSearchParams();
155
+ for (const [k, v] of Object.entries(f || {})) {
156
+ if (v !== undefined && v !== null && String(v) !== '')
157
+ qs.set(k, v instanceof Date ? v.toISOString() : String(v));
158
+ }
159
+ const s = qs.toString();
160
+ return this.http.req(`/v1/audit${s ? `?${s}` : ''}`);
96
161
  }
97
162
  // ---- API keys ----
98
- createApiKey(accountId, name = 'default', scopes = ['*']) {
99
- return this.http.req(`/v1/accounts/${encodeURIComponent(accountId)}/keys`, { method: 'POST', body: JSON.stringify({ name, scopes }) });
163
+ /**
164
+ * Create an API key. Scopes default to least-privilege read access
165
+ * (`*:read`); pass `['*']` explicitly for a full administrator key.
166
+ */
167
+ createApiKey(accountId, name = 'default', scopes = ['*:read'], opts = {}) {
168
+ return this.http.req(`/v1/accounts/${encodeURIComponent(accountId)}/keys`, {
169
+ method: 'POST',
170
+ body: JSON.stringify({ name, scopes, ...opts }),
171
+ });
100
172
  }
101
173
  revokeApiKey(keyId) {
102
174
  return this.http.req(`/v1/keys/${encodeURIComponent(keyId)}`, { method: 'DELETE' });
package/dist/index.d.ts CHANGED
@@ -8,6 +8,9 @@ import { QueueService } from './queue';
8
8
  import { StreamService } from './stream';
9
9
  import { InferenceService } from './inference';
10
10
  import { AiService } from './ai';
11
+ import { HealthService } from './health';
12
+ import { StorageService } from './storage';
13
+ import { NotificationsService } from './notifications';
11
14
  export * from './_http';
12
15
  export * from './iam';
13
16
  export * from './workflow';
@@ -18,6 +21,9 @@ export * from './faststore';
18
21
  export * from './queue';
19
22
  export * from './stream';
20
23
  export * from './inference';
24
+ export * from './health';
25
+ export * from './storage';
26
+ export * from './notifications';
21
27
  /** Legacy flat LDS signatures (the namespaced service drops the `lds` prefix). */
22
28
  export interface LdsLegacyMethods {
23
29
  ldsLicenses(): Promise<any[]>;
@@ -49,7 +55,17 @@ export declare class SynapseOneClient {
49
55
  readonly queue: QueueService;
50
56
  readonly stream: StreamService;
51
57
  readonly inference: InferenceService;
52
- constructor(opts: SynapseOneClientOptions);
58
+ readonly storage: StorageService;
59
+ readonly notifications: NotificationsService;
60
+ readonly health: HealthService;
61
+ readonly readiness: HealthService;
62
+ constructor(opts?: SynapseOneClientOptions);
63
+ /** Shallow gateway health. */
64
+ getHealth(opts?: import('./_http').RequestOptions): Promise<import("./health").HealthResponse>;
65
+ /** Aggregate readiness; returns the payload on 503 unless `throwOnDegraded`. */
66
+ getReady(opts?: import('./health').ReadyOptions): Promise<import("./health").ReadyResponse>;
67
+ /** Projected `{ok, tier1, upstreams}` status for Tier-2 readiness endpoints. */
68
+ getStatus(opts?: import('./health').ReadyOptions): Promise<import("./health").StatusResponse>;
53
69
  get baseUrl(): string;
54
70
  setToken(t: string): void;
55
71
  setApiKey(k: string): void;
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  "use strict";
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ // SPDX-FileCopyrightText: 2026 VKU.VIC
2
4
  /*
3
5
  * Copyright 2026 SynapseOne Contributors
4
6
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -46,6 +48,9 @@ const queue_1 = require("./queue");
46
48
  const stream_1 = require("./stream");
47
49
  const inference_1 = require("./inference");
48
50
  const ai_1 = require("./ai");
51
+ const health_1 = require("./health");
52
+ const storage_1 = require("./storage");
53
+ const notifications_1 = require("./notifications");
49
54
  __exportStar(require("./_http"), exports);
50
55
  __exportStar(require("./iam"), exports);
51
56
  __exportStar(require("./workflow"), exports);
@@ -56,6 +61,9 @@ __exportStar(require("./faststore"), exports);
56
61
  __exportStar(require("./queue"), exports);
57
62
  __exportStar(require("./stream"), exports);
58
63
  __exportStar(require("./inference"), exports);
64
+ __exportStar(require("./health"), exports);
65
+ __exportStar(require("./storage"), exports);
66
+ __exportStar(require("./notifications"), exports);
59
67
  class SynapseOneClient {
60
68
  http;
61
69
  iam;
@@ -67,7 +75,11 @@ class SynapseOneClient {
67
75
  queue;
68
76
  stream;
69
77
  inference;
70
- constructor(opts) {
78
+ storage;
79
+ notifications;
80
+ health;
81
+ readiness;
82
+ constructor(opts = {}) {
71
83
  this.http = new _http_1.HttpClient(opts);
72
84
  this.iam = new iam_1.IamService(this.http);
73
85
  this.workflow = new workflow_1.WorkflowService(this.http);
@@ -78,7 +90,18 @@ class SynapseOneClient {
78
90
  this.queue = new queue_1.QueueService(this.http);
79
91
  this.stream = new stream_1.StreamService(this.http);
80
92
  this.inference = new inference_1.InferenceService(this.http);
93
+ this.storage = new storage_1.StorageService(this.http);
94
+ this.notifications = new notifications_1.NotificationsService(this.http);
95
+ this.health = new health_1.HealthService(this.http);
96
+ // Alias: `client.readiness.ready()` reads naturally; same instance.
97
+ this.readiness = this.health;
81
98
  }
99
+ /** Shallow gateway health. */
100
+ getHealth(opts) { return this.health.health(opts); }
101
+ /** Aggregate readiness; returns the payload on 503 unless `throwOnDegraded`. */
102
+ getReady(opts) { return this.health.ready(opts); }
103
+ /** Projected `{ok, tier1, upstreams}` status for Tier-2 readiness endpoints. */
104
+ getStatus(opts) { return this.health.status(opts); }
82
105
  get baseUrl() { return this.http.baseUrl; }
83
106
  setToken(t) { this.http.setToken(t); }
84
107
  setApiKey(k) { this.http.setApiKey(k); }
@@ -91,7 +114,9 @@ class SynapseOneClient {
91
114
  exports.SynapseOneClient = SynapseOneClient;
92
115
  exports.SynapseDXClient = SynapseOneClient;
93
116
  const IAM_METHODS = [
94
- 'login', 'refresh', 'logout', 'logoutAll', 'verify', 'me',
117
+ 'login', 'register', 'loginWithPassword', 'changePassword', 'requestPasswordReset',
118
+ 'resetPassword', 'verifyEmail', 'requestEmailVerification', 'listSessions', 'revokeSession',
119
+ 'exchangeToken', 'refresh', 'logout', 'logoutAll', 'verify', 'me',
95
120
  'oidcConfig', 'oidcStart', 'oidcCallback',
96
121
  'listOrgs', 'createOrg', 'listAccounts', 'listUsers', 'createUser',
97
122
  'createAccount', 'userPolicies',
@@ -1,4 +1,4 @@
1
- import { HttpClient } from './_http';
1
+ import { HttpClient, RequestOptions } from './_http';
2
2
  export interface InferenceHealthResponse {
3
3
  status: string;
4
4
  service: string;
@@ -98,13 +98,36 @@ export declare class InferenceService {
98
98
  /** Check health and status of inference subsystems (LLM, embedding, decision). */
99
99
  health(): Promise<InferenceHealthResponse>;
100
100
  /** Run chat completions through the OpenRouter-backed LLM proxy. */
101
- chat(messages: ChatMessage[], opts?: ChatCompletionOptions): Promise<ChatCompletionResponse>;
101
+ chat(messages: ChatMessage[], opts?: ChatCompletionOptions, reqOpts?: RequestOptions): Promise<ChatCompletionResponse>;
102
+ /**
103
+ * Stream chat completions token-by-token (OpenAI-compatible SSE).
104
+ *
105
+ * Yields `{event, data}` records where `data` is the raw SSE payload; use
106
+ * {@link parseChatDelta} to read the incremental text. Pass `{signal}` to
107
+ * cancel the request (the underlying fetch is aborted).
108
+ *
109
+ * ```ts
110
+ * for await (const ev of client.inference.chatStream(msgs, {}, { signal })) {
111
+ * const delta = parseChatDelta(ev.data);
112
+ * if (delta) process.stdout.write(delta);
113
+ * }
114
+ * ```
115
+ */
116
+ chatStream(messages: ChatMessage[], opts?: ChatCompletionOptions, reqOpts?: RequestOptions): AsyncGenerator<{
117
+ event: string;
118
+ data: string;
119
+ }>;
102
120
  /** Generate text embeddings using the self-hosted lightweight model. */
103
121
  embeddings(input: string | string[], opts?: {
104
122
  model?: string;
105
- }): Promise<EmbeddingResponse>;
123
+ }, reqOpts?: RequestOptions): Promise<EmbeddingResponse>;
106
124
  /** Run structured question evaluation via Decision-2.0-Eos-0.8B (system_one). */
107
- decision(state: string, questions: Record<string, DecisionQuestion>): Promise<DecisionResponse>;
125
+ decision(state: string, questions: Record<string, DecisionQuestion>, reqOpts?: RequestOptions): Promise<DecisionResponse>;
108
126
  /** Semantic route an input text between candidate choices for workflow branching. */
109
- route(input: string, choices: Record<string, string>, instruction?: string): Promise<RouteResponse>;
127
+ route(input: string, choices: Record<string, string>, instruction?: string, reqOpts?: RequestOptions): Promise<RouteResponse>;
110
128
  }
129
+ /**
130
+ * Extract the incremental assistant text from one SSE `data:` payload.
131
+ * Returns `null` for `[DONE]`, keep-alives and frames without content.
132
+ */
133
+ export declare function parseChatDelta(data: string): string | null;
package/dist/inference.js CHANGED
@@ -1,4 +1,6 @@
1
1
  "use strict";
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ // SPDX-FileCopyrightText: 2026 VKU.VIC
2
4
  /*
3
5
  * Copyright 2026 SynapseOne Contributors
4
6
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -7,7 +9,11 @@
7
9
  */
8
10
  Object.defineProperty(exports, "__esModule", { value: true });
9
11
  exports.InferenceService = void 0;
12
+ exports.parseChatDelta = parseChatDelta;
13
+ const _http_1 = require("./_http");
10
14
  const P = '/v1/inference';
15
+ /** LLM calls are allowed 120s by the gateway route; the SDK must match it. */
16
+ const INFERENCE_OPTS = { timeoutMs: _http_1.INFERENCE_TIMEOUT_MS };
11
17
  class InferenceService {
12
18
  http;
13
19
  constructor(http) {
@@ -18,37 +24,57 @@ class InferenceService {
18
24
  return this.http.req(`${P}/health`);
19
25
  }
20
26
  /** Run chat completions through the OpenRouter-backed LLM proxy. */
21
- chat(messages, opts = {}) {
27
+ chat(messages, opts = {}, reqOpts = {}) {
22
28
  return this.http.req(`${P}/chat/completions`, {
23
29
  method: 'POST',
24
30
  body: JSON.stringify({
25
31
  messages,
26
32
  ...opts,
27
33
  }),
28
- });
34
+ }, { ...INFERENCE_OPTS, ...reqOpts });
35
+ }
36
+ /**
37
+ * Stream chat completions token-by-token (OpenAI-compatible SSE).
38
+ *
39
+ * Yields `{event, data}` records where `data` is the raw SSE payload; use
40
+ * {@link parseChatDelta} to read the incremental text. Pass `{signal}` to
41
+ * cancel the request (the underlying fetch is aborted).
42
+ *
43
+ * ```ts
44
+ * for await (const ev of client.inference.chatStream(msgs, {}, { signal })) {
45
+ * const delta = parseChatDelta(ev.data);
46
+ * if (delta) process.stdout.write(delta);
47
+ * }
48
+ * ```
49
+ */
50
+ chatStream(messages, opts = {}, reqOpts = {}) {
51
+ return this.http.stream(`${P}/chat/completions`, {
52
+ method: 'POST',
53
+ body: JSON.stringify({ messages, ...opts, stream: true }),
54
+ }, { ...INFERENCE_OPTS, ...reqOpts });
29
55
  }
30
56
  /** Generate text embeddings using the self-hosted lightweight model. */
31
- embeddings(input, opts = {}) {
57
+ embeddings(input, opts = {}, reqOpts = {}) {
32
58
  return this.http.req(`${P}/embeddings`, {
33
59
  method: 'POST',
34
60
  body: JSON.stringify({
35
61
  input,
36
62
  ...opts,
37
63
  }),
38
- });
64
+ }, { ...INFERENCE_OPTS, ...reqOpts });
39
65
  }
40
66
  /** Run structured question evaluation via Decision-2.0-Eos-0.8B (system_one). */
41
- decision(state, questions) {
67
+ decision(state, questions, reqOpts = {}) {
42
68
  return this.http.req(`${P}/decision`, {
43
69
  method: 'POST',
44
70
  body: JSON.stringify({
45
71
  state,
46
72
  questions,
47
73
  }),
48
- });
74
+ }, { ...INFERENCE_OPTS, ...reqOpts });
49
75
  }
50
76
  /** Semantic route an input text between candidate choices for workflow branching. */
51
- route(input, choices, instruction) {
77
+ route(input, choices, instruction, reqOpts = {}) {
52
78
  return this.http.req(`${P}/decision/route`, {
53
79
  method: 'POST',
54
80
  body: JSON.stringify({
@@ -56,7 +82,28 @@ class InferenceService {
56
82
  choices,
57
83
  ...(instruction ? { instruction } : {}),
58
84
  }),
59
- });
85
+ }, { ...INFERENCE_OPTS, ...reqOpts });
60
86
  }
61
87
  }
62
88
  exports.InferenceService = InferenceService;
89
+ /**
90
+ * Extract the incremental assistant text from one SSE `data:` payload.
91
+ * Returns `null` for `[DONE]`, keep-alives and frames without content.
92
+ */
93
+ function parseChatDelta(data) {
94
+ if (!data || data === '[DONE]')
95
+ return null;
96
+ try {
97
+ const j = JSON.parse(data);
98
+ const choice = j?.choices?.[0];
99
+ const delta = choice?.delta?.content ?? choice?.message?.content;
100
+ if (typeof delta === 'string' && delta.length)
101
+ return delta;
102
+ if (typeof j?.content === 'string' && j.content.length)
103
+ return j.content;
104
+ return null;
105
+ }
106
+ catch {
107
+ return data;
108
+ }
109
+ }
package/dist/lds.js CHANGED
@@ -1,4 +1,6 @@
1
1
  "use strict";
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ // SPDX-FileCopyrightText: 2026 VKU.VIC
2
4
  /*
3
5
  * Copyright 2026 SynapseOne Contributors
4
6
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -0,0 +1,70 @@
1
+ import { HttpClient } from './_http';
2
+ export type NotificationChannel = 'email' | 'sms' | 'webhook' | 'push' | 'log';
3
+ export type NotificationStatus = 'queued' | 'sending' | 'delivered' | 'failed' | 'dead' | 'cancelled';
4
+ export interface NotificationRecipient {
5
+ email?: string;
6
+ phone?: string;
7
+ userId?: string;
8
+ url?: string;
9
+ webhookUrl?: string;
10
+ pushToken?: string;
11
+ [key: string]: string | undefined;
12
+ }
13
+ export interface SendNotificationInput {
14
+ /** One channel, or several at once (fan-out creates one record per channel). */
15
+ channel?: NotificationChannel;
16
+ channels?: NotificationChannel[];
17
+ recipient: NotificationRecipient;
18
+ subject?: string;
19
+ /** Template with `{{var}}` placeholders filled from `data`. */
20
+ body: string;
21
+ data?: Record<string, unknown>;
22
+ idempotencyKey?: string;
23
+ }
24
+ export interface NotificationRecord {
25
+ id: string;
26
+ tenant: string;
27
+ channel: NotificationChannel;
28
+ recipient: NotificationRecipient;
29
+ subject: string;
30
+ body: string;
31
+ status: NotificationStatus;
32
+ attempts: Array<{
33
+ at: string;
34
+ ok: boolean;
35
+ error?: string;
36
+ }>;
37
+ idempotencyKey: string | null;
38
+ metadata: Record<string, unknown>;
39
+ createdAt: string;
40
+ updatedAt: string;
41
+ nextAttemptAt: string;
42
+ deadReason: string | null;
43
+ }
44
+ export interface NotificationPreference {
45
+ tenant: string;
46
+ recipient: string;
47
+ channels: NotificationChannel[];
48
+ unsubscribed: boolean;
49
+ updatedAt?: string;
50
+ }
51
+ export declare class NotificationsService {
52
+ private readonly http;
53
+ constructor(http: HttpClient);
54
+ /** Queue one notification per channel (202). */
55
+ send(input: SendNotificationInput): Promise<{
56
+ notifications: NotificationRecord[];
57
+ }>;
58
+ get(id: string): Promise<NotificationRecord>;
59
+ list(opts?: {
60
+ status?: NotificationStatus;
61
+ limit?: number;
62
+ }): Promise<NotificationRecord[]>;
63
+ cancel(id: string): Promise<NotificationRecord>;
64
+ getPreference(recipient: string): Promise<NotificationPreference>;
65
+ setPreference(recipient: string, prefs: {
66
+ channels?: NotificationChannel[];
67
+ unsubscribed?: boolean;
68
+ }): Promise<NotificationPreference>;
69
+ unsubscribe(recipient: string): Promise<NotificationPreference>;
70
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ // SPDX-FileCopyrightText: 2026 VKU.VIC
4
+ /*
5
+ * Copyright 2026 SynapseOne Contributors
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * SynapseNotifications client: outbox, templates, preferences and delivery.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.NotificationsService = void 0;
11
+ const P = '/v1/notifications';
12
+ class NotificationsService {
13
+ http;
14
+ constructor(http) {
15
+ this.http = http;
16
+ }
17
+ /** Queue one notification per channel (202). */
18
+ send(input) {
19
+ return this.http.req(P, {
20
+ method: 'POST',
21
+ body: JSON.stringify(input),
22
+ });
23
+ }
24
+ get(id) {
25
+ return this.http.req(`${P}/${encodeURIComponent(id)}`);
26
+ }
27
+ list(opts = {}) {
28
+ const qs = new URLSearchParams();
29
+ if (opts.status)
30
+ qs.set('status', opts.status);
31
+ if (opts.limit)
32
+ qs.set('limit', String(opts.limit));
33
+ const s = qs.toString();
34
+ return this.http.req(`${P}${s ? `?${s}` : ''}`);
35
+ }
36
+ cancel(id) {
37
+ return this.http.req(`${P}/${encodeURIComponent(id)}/cancel`, { method: 'POST', body: '{}' });
38
+ }
39
+ getPreference(recipient) {
40
+ return this.http.req(`${P}/preferences/${encodeURIComponent(recipient)}`);
41
+ }
42
+ setPreference(recipient, prefs) {
43
+ return this.http.req(`${P}/preferences/${encodeURIComponent(recipient)}`, {
44
+ method: 'PUT',
45
+ body: JSON.stringify(prefs),
46
+ });
47
+ }
48
+ unsubscribe(recipient) {
49
+ return this.http.req(`${P}/unsubscribe`, {
50
+ method: 'POST',
51
+ body: JSON.stringify({ recipient }),
52
+ });
53
+ }
54
+ }
55
+ exports.NotificationsService = NotificationsService;
@@ -0,0 +1 @@
1
+ export {};