@xmemory/temporal 1.0.0

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,53 @@
1
+ /**
2
+ * Configuration for the xmemory Temporal plugin.
3
+ *
4
+ * Nothing here carries secret material: {@link XmemoryConfig} holds the *name* of
5
+ * the env var supplying the API key, never the key. Workflow history is stored in
6
+ * the clear, so this stays safe to log and to persist.
7
+ */
8
+ export declare const DEFAULT_API_KEY_ENV = "XMEM_API_KEY";
9
+ export { DEFAULT_CLIENT_MARGIN_MS, DEFAULT_TIMEOUTS, clientTimeoutMs, type XmemoryTimeouts } from './defaults';
10
+ export interface XmemoryConfig {
11
+ instanceId: string;
12
+ url?: string;
13
+ /** Name of the env var holding the key — never the key itself. */
14
+ apiKeyEnv?: string;
15
+ /**
16
+ * How far below its Temporal deadline each call's client timeout sits. The
17
+ * budgets themselves belong to the workflow (`xmemoryForWorkflow`).
18
+ */
19
+ clientMarginMs?: number;
20
+ defaultExtractionLogic?: 'fast' | 'deep';
21
+ /**
22
+ * Log the server's `error_detail` verbatim when a write fails.
23
+ *
24
+ * Off by default: the detail can echo memory text or internal endpoints. Off, the
25
+ * log names the failed write and the detail's size.
26
+ */
27
+ logServerErrorDetail?: boolean;
28
+ }
29
+ /**
30
+ * Read the API key from the environment.
31
+ *
32
+ * Throws at worker start rather than on the first activity, so a misconfigured
33
+ * worker fails visibly.
34
+ */
35
+ export declare function resolveApiKey(config: XmemoryConfig, override?: string): string;
36
+ /**
37
+ * Where the client points when nothing is configured. Mirrors its unexported
38
+ * `DEFAULT_BASE_URL`: omitting `url` would let the client read
39
+ * `process.env.XMEM_API_URL` itself, reopening what `resolveUrl` closes.
40
+ */
41
+ export declare const DEFAULT_ENDPOINT = "https://api.xmemory.ai";
42
+ /** The endpoint to hand the client: always a value, never left to its own fallback. */
43
+ export declare function resolveEndpoint(config: XmemoryConfig): string;
44
+ /** Validate the effective endpoint, or `undefined` when none was supplied. */
45
+ export declare function resolveUrl(config: XmemoryConfig): string | undefined;
46
+ /**
47
+ * Return `candidate` if the API key may safely be sent to it.
48
+ *
49
+ * The key is a bearer token, so plaintext is refused off-box and credentials in the
50
+ * URL are refused outright. What the endpoint otherwise looks like — path, query,
51
+ * port — is the client's business, not this plugin's.
52
+ */
53
+ export declare function validateEndpoint(candidate: unknown, source: string): string;
package/dist/config.js ADDED
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ /**
3
+ * Configuration for the xmemory Temporal plugin.
4
+ *
5
+ * Nothing here carries secret material: {@link XmemoryConfig} holds the *name* of
6
+ * the env var supplying the API key, never the key. Workflow history is stored in
7
+ * the clear, so this stays safe to log and to persist.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.DEFAULT_ENDPOINT = exports.clientTimeoutMs = exports.DEFAULT_TIMEOUTS = exports.DEFAULT_CLIENT_MARGIN_MS = exports.DEFAULT_API_KEY_ENV = void 0;
11
+ exports.resolveApiKey = resolveApiKey;
12
+ exports.resolveEndpoint = resolveEndpoint;
13
+ exports.resolveUrl = resolveUrl;
14
+ exports.validateEndpoint = validateEndpoint;
15
+ exports.DEFAULT_API_KEY_ENV = 'XMEM_API_KEY';
16
+ // The client falls back to this when no url is passed, so it is an endpoint source
17
+ // this plugin must validate too.
18
+ const URL_ENV = 'XMEM_API_URL';
19
+ const LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]']);
20
+ // Re-exported from the workflow-safe leaf, so callers have one import site.
21
+ var defaults_1 = require("./defaults");
22
+ Object.defineProperty(exports, "DEFAULT_CLIENT_MARGIN_MS", { enumerable: true, get: function () { return defaults_1.DEFAULT_CLIENT_MARGIN_MS; } });
23
+ Object.defineProperty(exports, "DEFAULT_TIMEOUTS", { enumerable: true, get: function () { return defaults_1.DEFAULT_TIMEOUTS; } });
24
+ Object.defineProperty(exports, "clientTimeoutMs", { enumerable: true, get: function () { return defaults_1.clientTimeoutMs; } });
25
+ /**
26
+ * Read the API key from the environment.
27
+ *
28
+ * Throws at worker start rather than on the first activity, so a misconfigured
29
+ * worker fails visibly.
30
+ */
31
+ function resolveApiKey(config, override) {
32
+ // Passing a falsy key on is worse than failing: the client reads it as absent and
33
+ // falls back to XMEM_API_KEY, sending the ambient credential to this config's URL.
34
+ if (override !== undefined) {
35
+ if (typeof override !== 'string' || override === '') {
36
+ throw new Error(`xmemory apiKey was supplied but unusable (${override === '' ? 'empty' : typeof override}); ` +
37
+ 'omit it to read the environment');
38
+ }
39
+ return override;
40
+ }
41
+ // Defaulted only when genuinely absent: `??` would treat an own `null` as one.
42
+ const configured = config.apiKeyEnv;
43
+ const varName = configured === undefined ? exports.DEFAULT_API_KEY_ENV : configured;
44
+ if (typeof varName !== 'string' || varName === '') {
45
+ throw new Error(`xmemory apiKeyEnv must be a non-empty string, got ${varName === null ? 'null' : typeof varName}`);
46
+ }
47
+ const key = process.env[varName];
48
+ if (typeof key !== 'string' || key === '') {
49
+ throw new Error(`xmemory API key not found: environment variable ${JSON.stringify(varName)} is unset or empty. ` +
50
+ `Set it on the worker process, or pass { apiKey } to XmemoryPlugin.`);
51
+ }
52
+ return key;
53
+ }
54
+ /**
55
+ * Where the client points when nothing is configured. Mirrors its unexported
56
+ * `DEFAULT_BASE_URL`: omitting `url` would let the client read
57
+ * `process.env.XMEM_API_URL` itself, reopening what `resolveUrl` closes.
58
+ */
59
+ exports.DEFAULT_ENDPOINT = 'https://api.xmemory.ai';
60
+ /** The endpoint to hand the client: always a value, never left to its own fallback. */
61
+ function resolveEndpoint(config) {
62
+ return resolveUrl(config) ?? exports.DEFAULT_ENDPOINT;
63
+ }
64
+ /** Validate the effective endpoint, or `undefined` when none was supplied. */
65
+ function resolveUrl(config) {
66
+ const configured = config.url;
67
+ if (configured !== undefined)
68
+ return validateEndpoint(configured, 'xmemory url');
69
+ // With `url` unset the client falls back to this variable, which would then reach
70
+ // the wire unchecked. Resolving it here makes this the only path to an endpoint.
71
+ const fromEnv = process.env[URL_ENV];
72
+ if (fromEnv === undefined)
73
+ return undefined;
74
+ return validateEndpoint(fromEnv, `$${URL_ENV}`);
75
+ }
76
+ /**
77
+ * Return `candidate` if the API key may safely be sent to it.
78
+ *
79
+ * The key is a bearer token, so plaintext is refused off-box and credentials in the
80
+ * URL are refused outright. What the endpoint otherwise looks like — path, query,
81
+ * port — is the client's business, not this plugin's.
82
+ */
83
+ function validateEndpoint(candidate, source) {
84
+ if (typeof candidate !== 'string') {
85
+ throw new Error(`${source} must be a string, got ${candidate === null ? 'null' : typeof candidate}`);
86
+ }
87
+ // The trimmed value is what is returned: `new URL()` ignores surrounding
88
+ // whitespace, but the client concatenates this string with the request path.
89
+ const endpoint = candidate.trim();
90
+ if (endpoint === '') {
91
+ throw new Error(`${source} was supplied but empty; unset it to use the default endpoint`);
92
+ }
93
+ let parsed;
94
+ try {
95
+ parsed = new URL(endpoint);
96
+ }
97
+ catch {
98
+ // Not echoed: it may hold a secret.
99
+ throw new Error(`${source} is not a valid URL`);
100
+ }
101
+ if (parsed.username !== '' || parsed.password !== '') {
102
+ // Node rejects these at request time anyway, and the config stops being safe to log.
103
+ throw new Error(`${source} must not embed credentials; the API key is passed separately`);
104
+ }
105
+ if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
106
+ throw new Error(`${source} must use https (got scheme ${JSON.stringify(parsed.protocol)})`);
107
+ }
108
+ if (parsed.protocol === 'http:' && !LOOPBACK_HOSTS.has(parsed.hostname)) {
109
+ // The host is left out: it can name internal infrastructure.
110
+ throw new Error(`${source} must use https; the API key is sent as a bearer token. ` +
111
+ 'Plaintext http is accepted only for loopback hosts.');
112
+ }
113
+ return endpoint;
114
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * A total wall-clock bound for a client call.
3
+ *
4
+ * The xmemory client's `timeoutMs` stops applying once response headers arrive, so
5
+ * a stalled body can outlive the Activity deadline. Racing a timer caps the total.
6
+ */
7
+ export declare class DeadlineExceededError extends Error {
8
+ constructor(ms: number);
9
+ }
10
+ /** `ms` is a client budget, already positive and capped by `clientTimeoutMs`. */
11
+ export declare function withDeadline<T>(promise: Promise<T>, ms: number): Promise<T>;
12
+ export interface DeadlineInfo {
13
+ readonly startToCloseTimeoutMs: number;
14
+ readonly scheduleToCloseTimeoutMs: number;
15
+ readonly scheduledTimestampMs: number;
16
+ }
17
+ /**
18
+ * What is left of this Activity's deadline, or `null` if it has none. Zero or
19
+ * negative once spent.
20
+ *
21
+ * The smaller of the two bounds — a short schedule-to-close expires the attempt
22
+ * before a longer start-to-close would, and the larger figure would let a write
23
+ * commit after Temporal gave up. Schedule-to-close counts from the service's own
24
+ * `scheduledTimestampMs`; `Math.max(0, ...)` stops a lagging Worker clock, or a
25
+ * time-skipping test server, from *lengthening* the budget.
26
+ *
27
+ * `elapsedInAttemptMs` is for a caller running partway through the Activity, and
28
+ * applies to start-to-close only — schedule-to-close already contains that time.
29
+ * Time spent *before* the Activity function is not measurable without a
30
+ * Worker-wide interceptor, which this plugin does not install.
31
+ */
32
+ export declare function activityBudgetMs(info: DeadlineInfo, elapsedInAttemptMs?: number): number | null;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DeadlineExceededError = void 0;
4
+ exports.withDeadline = withDeadline;
5
+ exports.activityBudgetMs = activityBudgetMs;
6
+ /**
7
+ * A total wall-clock bound for a client call.
8
+ *
9
+ * The xmemory client's `timeoutMs` stops applying once response headers arrive, so
10
+ * a stalled body can outlive the Activity deadline. Racing a timer caps the total.
11
+ */
12
+ class DeadlineExceededError extends Error {
13
+ constructor(ms) {
14
+ super(`xmemory call exceeded its ${ms}ms client deadline`);
15
+ this.name = 'DeadlineExceededError';
16
+ }
17
+ }
18
+ exports.DeadlineExceededError = DeadlineExceededError;
19
+ /** `ms` is a client budget, already positive and capped by `clientTimeoutMs`. */
20
+ function withDeadline(promise, ms) {
21
+ let timer;
22
+ const deadline = new Promise((_, reject) => {
23
+ timer = setTimeout(() => reject(new DeadlineExceededError(ms)), ms);
24
+ });
25
+ return Promise.race([promise, deadline]).finally(() => clearTimeout(timer));
26
+ }
27
+ /**
28
+ * What is left of this Activity's deadline, or `null` if it has none. Zero or
29
+ * negative once spent.
30
+ *
31
+ * The smaller of the two bounds — a short schedule-to-close expires the attempt
32
+ * before a longer start-to-close would, and the larger figure would let a write
33
+ * commit after Temporal gave up. Schedule-to-close counts from the service's own
34
+ * `scheduledTimestampMs`; `Math.max(0, ...)` stops a lagging Worker clock, or a
35
+ * time-skipping test server, from *lengthening* the budget.
36
+ *
37
+ * `elapsedInAttemptMs` is for a caller running partway through the Activity, and
38
+ * applies to start-to-close only — schedule-to-close already contains that time.
39
+ * Time spent *before* the Activity function is not measurable without a
40
+ * Worker-wide interceptor, which this plugin does not install.
41
+ */
42
+ function activityBudgetMs(info, elapsedInAttemptMs = 0) {
43
+ const bounds = [];
44
+ if (info.startToCloseTimeoutMs > 0)
45
+ bounds.push(info.startToCloseTimeoutMs - elapsedInAttemptMs);
46
+ if (info.scheduleToCloseTimeoutMs > 0) {
47
+ bounds.push(info.scheduleToCloseTimeoutMs - Math.max(0, Date.now() - info.scheduledTimestampMs));
48
+ }
49
+ return bounds.length > 0 ? Math.min(...bounds) : null;
50
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Default activity budgets and the client-margin rule.
3
+ *
4
+ * A dependency-free leaf on purpose: workflow code imports these defaults, so
5
+ * this module must never reach for `process.env` or the xmemory client the way
6
+ * `config.ts` does.
7
+ */
8
+ /** Default `startToClose` budgets, in milliseconds. */
9
+ export interface XmemoryTimeouts {
10
+ readMs: number;
11
+ writeMs: number;
12
+ writeStartMs: number;
13
+ writeStatusMs: number;
14
+ }
15
+ /**
16
+ * The defaults `xmemoryForWorkflow()` applies when a workflow does not pass its
17
+ * own. The workflow owns the real budget: whatever it sets is what Temporal
18
+ * enforces, and what each activity derives its client timeout from.
19
+ */
20
+ export declare const DEFAULT_TIMEOUTS: XmemoryTimeouts;
21
+ /**
22
+ * How far below its Temporal deadline each call's client timeout sits. Inverted,
23
+ * Temporal could abandon an enqueue that still succeeds server-side, and a later
24
+ * durable-write retry would queue it twice.
25
+ */
26
+ export declare const DEFAULT_CLIENT_MARGIN_MS = 5000;
27
+ /**
28
+ * The longest a Node timer can hold (24.8 days).
29
+ *
30
+ * `setTimeout` silently turns anything larger into 1ms. Client budgets are capped
31
+ * at it, and `writeDurable` refuses loop options past it before its enqueue.
32
+ */
33
+ export declare const MAX_DURATION_MS = 2147483647;
34
+ /**
35
+ * Client budget for an activity whose Temporal deadline is `activityMs`.
36
+ *
37
+ * Always strictly below it, so the client gives up first and the failure is an
38
+ * attributable xmemory error rather than an opaque activity timeout. A budget at
39
+ * or under the margin gets a proportional one instead; there is no floor, which
40
+ * would hand back more time than Temporal is giving.
41
+ *
42
+ * Only meaningful above timer resolution: a 1ms deadline yields 0.8ms, which
43
+ * `setTimeout` rounds back to 1ms — the same instant Temporal uses.
44
+ *
45
+ * Capped at `MAX_DURATION_MS`, because the client arms a `setTimeout` with this
46
+ * value and Node fires anything larger after 1ms. The cap only applies to a deadline
47
+ * longer than that, so the client still gives up first.
48
+ */
49
+ export declare function clientTimeoutMs(activityMs: number, marginMs?: number): number;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ /**
3
+ * Default activity budgets and the client-margin rule.
4
+ *
5
+ * A dependency-free leaf on purpose: workflow code imports these defaults, so
6
+ * this module must never reach for `process.env` or the xmemory client the way
7
+ * `config.ts` does.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.MAX_DURATION_MS = exports.DEFAULT_CLIENT_MARGIN_MS = exports.DEFAULT_TIMEOUTS = void 0;
11
+ exports.clientTimeoutMs = clientTimeoutMs;
12
+ /**
13
+ * The defaults `xmemoryForWorkflow()` applies when a workflow does not pass its
14
+ * own. The workflow owns the real budget: whatever it sets is what Temporal
15
+ * enforces, and what each activity derives its client timeout from.
16
+ */
17
+ exports.DEFAULT_TIMEOUTS = {
18
+ readMs: 120_000,
19
+ writeMs: 180_000,
20
+ writeStartMs: 30_000,
21
+ writeStatusMs: 30_000,
22
+ };
23
+ /**
24
+ * How far below its Temporal deadline each call's client timeout sits. Inverted,
25
+ * Temporal could abandon an enqueue that still succeeds server-side, and a later
26
+ * durable-write retry would queue it twice.
27
+ */
28
+ exports.DEFAULT_CLIENT_MARGIN_MS = 5_000;
29
+ /**
30
+ * The longest a Node timer can hold (24.8 days).
31
+ *
32
+ * `setTimeout` silently turns anything larger into 1ms. Client budgets are capped
33
+ * at it, and `writeDurable` refuses loop options past it before its enqueue.
34
+ */
35
+ exports.MAX_DURATION_MS = 2_147_483_647;
36
+ /**
37
+ * Client budget for an activity whose Temporal deadline is `activityMs`.
38
+ *
39
+ * Always strictly below it, so the client gives up first and the failure is an
40
+ * attributable xmemory error rather than an opaque activity timeout. A budget at
41
+ * or under the margin gets a proportional one instead; there is no floor, which
42
+ * would hand back more time than Temporal is giving.
43
+ *
44
+ * Only meaningful above timer resolution: a 1ms deadline yields 0.8ms, which
45
+ * `setTimeout` rounds back to 1ms — the same instant Temporal uses.
46
+ *
47
+ * Capped at `MAX_DURATION_MS`, because the client arms a `setTimeout` with this
48
+ * value and Node fires anything larger after 1ms. The cap only applies to a deadline
49
+ * longer than that, so the client still gives up first.
50
+ */
51
+ function clientTimeoutMs(activityMs, marginMs = exports.DEFAULT_CLIENT_MARGIN_MS) {
52
+ if (!Number.isFinite(activityMs) || activityMs <= 0) {
53
+ throw new RangeError(`activityMs must be a positive, finite number, got ${activityMs}`);
54
+ }
55
+ const margin = Number.isFinite(marginMs) && marginMs > 0 ? marginMs : 0;
56
+ const budgetMs = margin === 0 || activityMs <= margin ? activityMs * 0.8 : activityMs - margin;
57
+ return Math.min(budgetMs, exports.MAX_DURATION_MS);
58
+ }
package/dist/dto.d.ts ADDED
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Activity input and output types.
3
+ *
4
+ * The `*Input` / `*Output` shapes are ours: activity payloads are persisted
5
+ * verbatim into workflow history, so they are a compatibility contract for every
6
+ * workflow that has ever run, and owning them lets a result field be added or
7
+ * renamed upstream without breaking replay. What we send through untransformed —
8
+ * mutations, read modes, scopes — is the client's own type.
9
+ */
10
+ import type { AsyncWriteResult, ReadMode, ReadResult, ReadScope, WriteMutation, WriteResult, WriteStatusResult } from 'xmemory';
11
+ export type { ObjectMutationBody, ReadMode, ReadScope, RelationEndpoint, RelationMutationBody, RelationsScope, ScopeObject, WriteMutation, } from 'xmemory';
12
+ export interface ReadInput {
13
+ query: string;
14
+ readMode?: ReadMode;
15
+ scope?: ReadScope;
16
+ }
17
+ export interface SubAnswer {
18
+ subQuery: string;
19
+ readerResult: unknown;
20
+ error: string | null;
21
+ }
22
+ export interface ReadOutput {
23
+ readerResult: unknown;
24
+ subAnswers: SubAnswer[];
25
+ traceId: string | null;
26
+ }
27
+ /** Either free `text` for the extractor, or explicit `structuredMutations`. */
28
+ export interface WriteInput {
29
+ text: string;
30
+ extractionLogic?: string;
31
+ diffEngine?: boolean;
32
+ structuredMutations?: readonly WriteMutation[];
33
+ }
34
+ export interface WriteOutput {
35
+ writeId: string;
36
+ traceId: string | null;
37
+ changes: unknown;
38
+ }
39
+ export interface WriteStartOutput {
40
+ writeId: string;
41
+ }
42
+ export interface WriteStatusInput {
43
+ writeId: string;
44
+ }
45
+ export interface WriteStatusOutput {
46
+ writeId: string;
47
+ writeStatus: string;
48
+ completedAt: string | null;
49
+ changes: unknown;
50
+ }
51
+ export declare function projectRead(result: ReadResult): ReadOutput;
52
+ export declare function projectWrite(result: WriteResult): WriteOutput;
53
+ export declare function projectWriteStart(result: AsyncWriteResult): WriteStartOutput;
54
+ export declare function projectWriteStatus(result: WriteStatusResult): WriteStatusOutput;
package/dist/dto.js ADDED
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ /**
3
+ * Activity input and output types.
4
+ *
5
+ * The `*Input` / `*Output` shapes are ours: activity payloads are persisted
6
+ * verbatim into workflow history, so they are a compatibility contract for every
7
+ * workflow that has ever run, and owning them lets a result field be added or
8
+ * renamed upstream without breaking replay. What we send through untransformed —
9
+ * mutations, read modes, scopes — is the client's own type.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.projectRead = projectRead;
13
+ exports.projectWrite = projectWrite;
14
+ exports.projectWriteStart = projectWriteStart;
15
+ exports.projectWriteStatus = projectWriteStatus;
16
+ // --- Projections from the client's result shapes ---------------------------
17
+ //
18
+ // Required fields are checked rather than defaulted, so a malformed response fails
19
+ // as a transport error instead of reading as an empty answer or an unknown status.
20
+ function ownField(result, field) {
21
+ return typeof result === 'object' && result !== null && Object.hasOwn(result, field)
22
+ ? result[field]
23
+ : undefined;
24
+ }
25
+ function requiredString(result, field) {
26
+ const value = ownField(result, field);
27
+ // Non-empty: an empty id passes every type check and names no write.
28
+ if (typeof value !== 'string' || value === '') {
29
+ throw new Error(`xmemory response has no usable ${field}`);
30
+ }
31
+ return value;
32
+ }
33
+ function optionalString(result, field) {
34
+ const value = ownField(result, field);
35
+ return typeof value === 'string' ? value : null;
36
+ }
37
+ /** Present, whatever its type. `reader_result` may legitimately be null. */
38
+ function requiredField(result, field) {
39
+ if (typeof result !== 'object' || result === null || !Object.hasOwn(result, field)) {
40
+ throw new Error(`xmemory response has no ${field}`);
41
+ }
42
+ return result[field];
43
+ }
44
+ function projectRead(result) {
45
+ // Required, not defaulted: turning a missing answer into `undefined` hands the
46
+ // workflow a confident empty one. The client normalizes `reader_results` to an
47
+ // array, so a non-array here did not come from a client that did.
48
+ const readerResult = requiredField(result, 'reader_result');
49
+ const subAnswers = requiredField(result, 'reader_results');
50
+ if (!Array.isArray(subAnswers)) {
51
+ throw new Error('xmemory response has a malformed reader_results');
52
+ }
53
+ return {
54
+ readerResult,
55
+ subAnswers: subAnswers.map((r) => ({
56
+ subQuery: requiredString(r, 'sub_query'),
57
+ readerResult: requiredField(r, 'reader_result'),
58
+ error: optionalString(r, 'error'),
59
+ })),
60
+ traceId: optionalString(result, 'trace_id'),
61
+ };
62
+ }
63
+ function projectWrite(result) {
64
+ return {
65
+ writeId: requiredString(result, 'write_id'),
66
+ traceId: optionalString(result, 'trace_id'),
67
+ changes: ownField(result, 'changes') ?? null,
68
+ };
69
+ }
70
+ function projectWriteStart(result) {
71
+ return { writeId: requiredString(result, 'write_id') };
72
+ }
73
+ function projectWriteStatus(result) {
74
+ return {
75
+ writeId: requiredString(result, 'write_id'),
76
+ // Required: the durable loop decides a write is done by this value.
77
+ writeStatus: requiredString(result, 'write_status'),
78
+ completedAt: optionalString(result, 'completed_at'),
79
+ // Not surfaced by the client yet; populates itself if a later release adds it.
80
+ // `null`, never absent: an omitted field vanishes in JSON.
81
+ changes: ownField(result, 'changes') ?? ownField(result, 'result') ?? null,
82
+ };
83
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Translate xmemory API errors into Temporal failures.
3
+ *
4
+ * Temporal owns retries, so this is the one place an `XmemoryAPIError` becomes an
5
+ * `ApplicationFailure` with a retryability verdict.
6
+ *
7
+ * Rules: branch on `.code`, not the HTTP status; an unrecognized code never raises
8
+ * and keeps its own type, so a newer server cannot break this client mid-deploy,
9
+ * while whether to retry it comes from the status — a 401 or 404 is terminal
10
+ * whatever the code says; never echo the raw exception string, which can embed
11
+ * internal hostnames.
12
+ */
13
+ import { ApplicationFailure } from '@temporalio/common';
14
+ import { TYPE_AUTH_FAILED, TYPE_BAD_OPTIONS, TYPE_BAD_REQUEST, TYPE_DAILY_QUOTA_EXCEEDED, TYPE_DEADLINE_EXPIRED, TYPE_MONTHLY_QUOTA_EXCEEDED, TYPE_NOT_BOUND, TYPE_NOT_FOUND, TYPE_NO_DEADLINE, TYPE_QUOTA_EXCEEDED, TYPE_RATE_LIMITED, TYPE_SCHEMA_REJECTED, TYPE_SERVER_ERROR, TYPE_UNAVAILABLE, TYPE_UNKNOWN, TYPE_WRITE_FAILED, TYPE_WRITE_NOT_FOUND, TYPE_WRITE_TIMEOUT } from './names';
15
+ export { TYPE_AUTH_FAILED, TYPE_BAD_OPTIONS, TYPE_BAD_REQUEST, TYPE_DAILY_QUOTA_EXCEEDED, TYPE_DEADLINE_EXPIRED, TYPE_MONTHLY_QUOTA_EXCEEDED, TYPE_NOT_BOUND, TYPE_NOT_FOUND, TYPE_NO_DEADLINE, TYPE_QUOTA_EXCEEDED, TYPE_RATE_LIMITED, TYPE_SCHEMA_REJECTED, TYPE_SERVER_ERROR, TYPE_UNAVAILABLE, TYPE_UNKNOWN, TYPE_WRITE_FAILED, TYPE_WRITE_NOT_FOUND, TYPE_WRITE_TIMEOUT, };
16
+ export declare const NON_RETRYABLE_TYPES: readonly string[];
17
+ /** Map any client-raised error onto a Temporal `ApplicationFailure`. */
18
+ export declare function toApplicationFailure(err: unknown): ApplicationFailure;