@maximem/synap-js-sdk 0.4.1 → 0.4.3
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/bin/synap-js-sdk.js +3 -3
- package/dist/anticipation-cache-ARYEEKMU.cjs.map +1 -1
- package/dist/chunk-HR5Y3KXS.cjs.map +1 -1
- package/dist/chunk-RB57I2IQ.cjs.map +1 -1
- package/dist/chunk-VXRCBMCY.cjs.map +1 -1
- package/dist/chunk-ZKOJDUN4.cjs.map +1 -1
- package/dist/env-55ULHNJZ.cjs.map +1 -1
- package/dist/grpc/index.cjs.map +1 -1
- package/dist/index.cjs +63 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +62 -4
- package/dist/index.js.map +1 -1
- package/dist/stream-client-ZN6ADZBR.cjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -299,6 +299,18 @@ declare class HttpTransport {
|
|
|
299
299
|
currentCredentials(): Credentials;
|
|
300
300
|
/** Write back identity resolved from `GET /api/v1/auth/whoami`. */
|
|
301
301
|
updateCredentials(patch: Partial<Credentials>): void;
|
|
302
|
+
/**
|
|
303
|
+
* The instance's scoping mode, from whoami. Lives here rather than on the
|
|
304
|
+
* client because every interface factory already receives the transport,
|
|
305
|
+
* and it is per-client, so a module-level global would be wrong the moment a
|
|
306
|
+
* process holds two clients.
|
|
307
|
+
*
|
|
308
|
+
* undefined means the server did not report it, which is true of any
|
|
309
|
+
* deployment older than the field, and must mean "enforce nothing".
|
|
310
|
+
*/
|
|
311
|
+
userContextIsolation: string | undefined;
|
|
312
|
+
/** Refuse a customer_id on a B2C instance, at the call site. */
|
|
313
|
+
checkCustomerId(customerId: unknown, where: string): void;
|
|
302
314
|
setTimeouts(timeouts: Partial<TimeoutConfig>): void;
|
|
303
315
|
/** `null` restores the default policy, matching Python's `retry_policy=None`. */
|
|
304
316
|
setRetryPolicy(policy: Partial<RetryPolicy> | null): void;
|
package/dist/index.d.ts
CHANGED
|
@@ -299,6 +299,18 @@ declare class HttpTransport {
|
|
|
299
299
|
currentCredentials(): Credentials;
|
|
300
300
|
/** Write back identity resolved from `GET /api/v1/auth/whoami`. */
|
|
301
301
|
updateCredentials(patch: Partial<Credentials>): void;
|
|
302
|
+
/**
|
|
303
|
+
* The instance's scoping mode, from whoami. Lives here rather than on the
|
|
304
|
+
* client because every interface factory already receives the transport,
|
|
305
|
+
* and it is per-client, so a module-level global would be wrong the moment a
|
|
306
|
+
* process holds two clients.
|
|
307
|
+
*
|
|
308
|
+
* undefined means the server did not report it, which is true of any
|
|
309
|
+
* deployment older than the field, and must mean "enforce nothing".
|
|
310
|
+
*/
|
|
311
|
+
userContextIsolation: string | undefined;
|
|
312
|
+
/** Refuse a customer_id on a B2C instance, at the call site. */
|
|
313
|
+
checkCustomerId(customerId: unknown, where: string): void;
|
|
302
314
|
setTimeouts(timeouts: Partial<TimeoutConfig>): void;
|
|
303
315
|
/** `null` restores the default policy, matching Python's `retry_policy=None`. */
|
|
304
316
|
setRetryPolicy(policy: Partial<RetryPolicy> | null): void;
|
package/dist/index.js
CHANGED
|
@@ -200,6 +200,16 @@ var Heartbeat = class {
|
|
|
200
200
|
}
|
|
201
201
|
};
|
|
202
202
|
|
|
203
|
+
// src/scoping.ts
|
|
204
|
+
var B2C_ISOLATION = "equals_customer";
|
|
205
|
+
function checkCustomerId(isolation, customerId, where, instanceId) {
|
|
206
|
+
if (customerId === void 0 || customerId === null || customerId === "") return;
|
|
207
|
+
if (isolation !== B2C_ISOLATION) return;
|
|
208
|
+
throw new InvalidInputError(
|
|
209
|
+
`${where}: customer_id is not accepted on this instance${instanceId ? ` (${instanceId})` : ""}, which is B2C (user_context_isolation='${B2C_ISOLATION}'). Send user_id only; it is the whole identity, and the server files and reads your data under it. Received customer_id=${JSON.stringify(customerId)}.`
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
203
213
|
// src/transport/http.ts
|
|
204
214
|
var DEFAULT_BASE_URL = "https://synap-cloud-prod.maximem.ai";
|
|
205
215
|
var DEFAULT_TIMEOUTS = { connect: 5, read: 30, write: 10 };
|
|
@@ -253,6 +263,25 @@ var HttpTransport = class {
|
|
|
253
263
|
updateCredentials(patch) {
|
|
254
264
|
this.#credentials = { ...this.#credentials, ...patch };
|
|
255
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* The instance's scoping mode, from whoami. Lives here rather than on the
|
|
268
|
+
* client because every interface factory already receives the transport,
|
|
269
|
+
* and it is per-client, so a module-level global would be wrong the moment a
|
|
270
|
+
* process holds two clients.
|
|
271
|
+
*
|
|
272
|
+
* undefined means the server did not report it, which is true of any
|
|
273
|
+
* deployment older than the field, and must mean "enforce nothing".
|
|
274
|
+
*/
|
|
275
|
+
userContextIsolation = void 0;
|
|
276
|
+
/** Refuse a customer_id on a B2C instance, at the call site. */
|
|
277
|
+
checkCustomerId(customerId, where) {
|
|
278
|
+
checkCustomerId(
|
|
279
|
+
this.userContextIsolation,
|
|
280
|
+
customerId,
|
|
281
|
+
where,
|
|
282
|
+
this.#credentials.instanceId
|
|
283
|
+
);
|
|
284
|
+
}
|
|
256
285
|
setTimeouts(timeouts) {
|
|
257
286
|
this.timeouts = { ...this.timeouts, ...timeouts };
|
|
258
287
|
}
|
|
@@ -527,6 +556,10 @@ function buildFetchRequest(scope, options) {
|
|
|
527
556
|
}
|
|
528
557
|
async function fetchContext(transport, scope, options = {}, hooks = {}) {
|
|
529
558
|
const startedAt = Date.now();
|
|
559
|
+
transport.checkCustomerId(
|
|
560
|
+
opt(options, "customer_id", "customerId"),
|
|
561
|
+
`${scope}.context.fetch`
|
|
562
|
+
);
|
|
530
563
|
const anticipated = hooks.beforeFetch?.(scope, options) ?? null;
|
|
531
564
|
if (anticipated !== null) {
|
|
532
565
|
hooks.onServedFromCache?.(scope, options, anticipated);
|
|
@@ -787,6 +820,8 @@ var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "error",
|
|
|
787
820
|
function createMemoriesInterface(transport) {
|
|
788
821
|
return {
|
|
789
822
|
async create(options) {
|
|
823
|
+
const o2 = options;
|
|
824
|
+
transport.checkCustomerId(o2["customer_id"] ?? o2["customerId"], "memories.create");
|
|
790
825
|
return transport.request("memories_create", { body: buildCreateBody(options) });
|
|
791
826
|
},
|
|
792
827
|
async batch_create(options) {
|
|
@@ -803,7 +838,7 @@ function createMemoriesInterface(transport) {
|
|
|
803
838
|
const userId = options.user_id ?? options.userId;
|
|
804
839
|
const customerId = options.customer_id ?? options.customerId;
|
|
805
840
|
if (!userId) throw new InvalidInputError("user_id is required");
|
|
806
|
-
|
|
841
|
+
transport.checkCustomerId(customerId, "memories.create_from_file");
|
|
807
842
|
const filePath = options.file_path ?? options.filePath;
|
|
808
843
|
const sources = [filePath, options.file, options.text].filter((v) => v !== void 0);
|
|
809
844
|
if (sources.length === 0) {
|
|
@@ -946,7 +981,6 @@ function buildRecordMessageBody(options) {
|
|
|
946
981
|
throw new InvalidInputError("content is required");
|
|
947
982
|
}
|
|
948
983
|
if (!userId) throw new InvalidInputError("user_id is required");
|
|
949
|
-
if (!customerId) throw new InvalidInputError("customer_id is required");
|
|
950
984
|
const body = {
|
|
951
985
|
conversation_id: conversationId,
|
|
952
986
|
role: options.role,
|
|
@@ -1051,6 +1085,10 @@ function createConversationNamespace(transport, subscribers = new CompactionSubs
|
|
|
1051
1085
|
};
|
|
1052
1086
|
return {
|
|
1053
1087
|
async record_message(options) {
|
|
1088
|
+
transport.checkCustomerId(
|
|
1089
|
+
options["customer_id"] ?? options["customerId"],
|
|
1090
|
+
"conversation.record_message"
|
|
1091
|
+
);
|
|
1054
1092
|
const body = buildRecordMessageBody(options);
|
|
1055
1093
|
const result = await transport.request("conversations_messages", { body });
|
|
1056
1094
|
onTurn({
|
|
@@ -1069,6 +1107,13 @@ function createConversationNamespace(transport, subscribers = new CompactionSubs
|
|
|
1069
1107
|
if (!Array.isArray(messages) || messages.length === 0) {
|
|
1070
1108
|
throw new InvalidInputError("messages must be a non-empty array");
|
|
1071
1109
|
}
|
|
1110
|
+
for (const m of messages) {
|
|
1111
|
+
const mo = m;
|
|
1112
|
+
transport.checkCustomerId(
|
|
1113
|
+
mo["customer_id"] ?? mo["customerId"],
|
|
1114
|
+
"conversation.record_messages_batch"
|
|
1115
|
+
);
|
|
1116
|
+
}
|
|
1072
1117
|
const bodies = messages.map(buildRecordMessageBody);
|
|
1073
1118
|
const result = await transport.request("conversations_messages_batch", {
|
|
1074
1119
|
body: { messages: bodies }
|
|
@@ -1238,7 +1283,7 @@ function envPort() {
|
|
|
1238
1283
|
return Number.isFinite(parsed) ? parsed : void 0;
|
|
1239
1284
|
}
|
|
1240
1285
|
function envTls() {
|
|
1241
|
-
const raw = getEnv("SYNAP_GRPC_USE_TLS");
|
|
1286
|
+
const raw = getEnv("SYNAP_GRPC_USE_TLS") ?? getEnv("SYNAP_GRPC_TLS");
|
|
1242
1287
|
if (raw === void 0 || raw === "") return void 0;
|
|
1243
1288
|
return !["0", "false", "no", "off"].includes(raw.trim().toLowerCase());
|
|
1244
1289
|
}
|
|
@@ -2168,6 +2213,7 @@ var SynapClient = class {
|
|
|
2168
2213
|
#fetchHooks;
|
|
2169
2214
|
constructor(options = {}) {
|
|
2170
2215
|
const credentials = resolveCredentials(options);
|
|
2216
|
+
const resolvedBaseUrl = resolveBaseUrl(options);
|
|
2171
2217
|
validateInstanceId(credentials.instanceId);
|
|
2172
2218
|
const registryKey = buildRegistryKey(credentials.instanceId, credentials.apiKey);
|
|
2173
2219
|
if (options._force_new !== true) {
|
|
@@ -2185,7 +2231,12 @@ var SynapClient = class {
|
|
|
2185
2231
|
this.#registryKeys = options._force_new === true ? [] : [registryKey];
|
|
2186
2232
|
this.#transport = new HttpTransport({
|
|
2187
2233
|
credentials,
|
|
2188
|
-
|
|
2234
|
+
// SYNAP_BASE_URL is honoured, as Python does (sdk.py) and as the 0.3.x
|
|
2235
|
+
// wrapper did. Ignoring it was a wrong-DESTINATION bug rather than a
|
|
2236
|
+
// missing convenience: anyone pointing at a self-hosted or staging
|
|
2237
|
+
// deployment through the environment would silently have started sending
|
|
2238
|
+
// their data to production on upgrade.
|
|
2239
|
+
...resolvedBaseUrl !== void 0 ? { baseUrl: resolvedBaseUrl } : {},
|
|
2189
2240
|
...options.timeouts !== void 0 ? { timeouts: options.timeouts } : {},
|
|
2190
2241
|
...options.retryPolicy !== void 0 ? { retryPolicy: options.retryPolicy } : {},
|
|
2191
2242
|
...options.keepAlive !== void 0 ? { keepAlive: options.keepAlive } : {},
|
|
@@ -2371,6 +2422,8 @@ var SynapClient = class {
|
|
|
2371
2422
|
const whoami = await this.#transport.request("whoami", {});
|
|
2372
2423
|
const resolvedClientId = String(whoami["client_id"] ?? "");
|
|
2373
2424
|
const resolvedInstanceId = String(whoami["instance_id"] ?? "");
|
|
2425
|
+
const iso2 = whoami["user_context_isolation"];
|
|
2426
|
+
this.#transport.userContextIsolation = typeof iso2 === "string" ? iso2 : void 0;
|
|
2374
2427
|
this.#transport.updateCredentials({
|
|
2375
2428
|
...resolvedClientId !== "" && credentials.clientId === "" ? { clientId: resolvedClientId } : {},
|
|
2376
2429
|
...resolvedInstanceId !== "" && credentials.instanceId === "" ? { instanceId: resolvedInstanceId } : {}
|
|
@@ -2635,6 +2688,11 @@ function pick3(o2, snake, camel) {
|
|
|
2635
2688
|
const a = o2[snake];
|
|
2636
2689
|
return a !== void 0 ? a : o2[camel];
|
|
2637
2690
|
}
|
|
2691
|
+
function resolveBaseUrl(options) {
|
|
2692
|
+
if (options.baseUrl !== void 0) return options.baseUrl;
|
|
2693
|
+
const fromEnv = getEnv("SYNAP_BASE_URL")?.trim();
|
|
2694
|
+
return fromEnv !== void 0 && fromEnv !== "" ? fromEnv : void 0;
|
|
2695
|
+
}
|
|
2638
2696
|
function resolveCredentials(options) {
|
|
2639
2697
|
const apiKey = options.apiKey ?? getEnv("SYNAP_API_KEY");
|
|
2640
2698
|
if (!apiKey) {
|