@superdoc/sdk 2.6.0 → 2.8.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.
Files changed (51) hide show
  1. package/dist/agent/actions.cjs +1869 -160
  2. package/dist/agent/actions.d.ts +77 -10
  3. package/dist/agent/actions.js +1870 -161
  4. package/dist/agent/catalog.cjs +102 -9
  5. package/dist/agent/catalog.d.ts +243 -0
  6. package/dist/agent/catalog.js +99 -9
  7. package/dist/agent/doc-snapshot.cjs +200 -2
  8. package/dist/agent/doc-snapshot.d.ts +91 -0
  9. package/dist/agent/doc-snapshot.js +199 -2
  10. package/dist/agent/runtime.cjs +9 -1
  11. package/dist/agent/runtime.d.ts +8 -0
  12. package/dist/agent/runtime.js +9 -1
  13. package/dist/generated/client.cjs +754 -770
  14. package/dist/generated/client.d.ts +10 -9
  15. package/dist/generated/client.js +754 -770
  16. package/dist/generated/contract.cjs +16178 -272
  17. package/dist/generated/contract.d.ts +38 -0
  18. package/dist/generated/contract.js +17419 -1510
  19. package/dist/index.cjs +6 -5
  20. package/dist/index.d.ts +2 -2
  21. package/dist/index.js +6 -5
  22. package/dist/introspection.cjs +59 -0
  23. package/dist/introspection.d.ts +3 -0
  24. package/dist/introspection.js +53 -0
  25. package/dist/runtime/document-rpc.cjs +179 -40
  26. package/dist/runtime/document-rpc.d.ts +13 -4
  27. package/dist/runtime/document-rpc.js +175 -40
  28. package/dist/runtime/embedded-cli.cjs +5 -68
  29. package/dist/runtime/embedded-cli.js +5 -67
  30. package/dist/runtime/embedded-document-host.cjs +28 -0
  31. package/dist/runtime/embedded-document-host.d.ts +1 -0
  32. package/dist/runtime/embedded-document-host.js +23 -0
  33. package/dist/runtime/embedded-platform.cjs +108 -0
  34. package/dist/runtime/embedded-platform.d.ts +5 -0
  35. package/dist/runtime/embedded-platform.js +99 -0
  36. package/dist/runtime/host.cjs +237 -24
  37. package/dist/runtime/host.d.ts +17 -0
  38. package/dist/runtime/host.js +237 -25
  39. package/dist/runtime/process.cjs +30 -9
  40. package/dist/runtime/process.d.ts +9 -0
  41. package/dist/runtime/process.js +29 -9
  42. package/dist/runtime/sdk-version.generated.cjs +8 -0
  43. package/dist/runtime/sdk-version.generated.d.ts +1 -0
  44. package/dist/runtime/sdk-version.generated.js +4 -0
  45. package/dist/runtime/transport-common.cjs +1 -0
  46. package/dist/runtime/transport-common.d.ts +28 -10
  47. package/dist/runtime/transport-common.js +1 -1
  48. package/package.json +7 -7
  49. package/tools/__pycache__/__init__.cpython-311.pyc +0 -0
  50. package/tools/__pycache__/intent_dispatch_generated.cpython-311.pyc +0 -0
  51. package/tools/tools-policy.json +1 -1
@@ -2,7 +2,7 @@ import { spawn } from 'node:child_process';
2
2
  import { createInterface } from 'node:readline';
3
3
  import { buildOperationArgv, resolveInvocation, } from './transport-common.js';
4
4
  import { SuperDocCliError } from './errors.js';
5
- import { DOCUMENT_RPC_FEATURES, buildDocumentInvokeParams, buildDocumentOpenParams, canOpenWithDocumentRpc, documentRpcEnabled, mapDocumentLifecycleResult, mapDocumentOpenResult, supportsDocumentRpc, } from './document-rpc.js';
5
+ import { DOCUMENT_RPC_FEATURES, DOCUMENT_RPC_DEFAULT_CHANGE_MODE_FEATURE, DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE, DOCUMENT_RPC_SOURCE_SAVE_FEATURE, buildDocumentInvokeParams, buildDocumentOpenParams, canOpenWithDocumentRpc, documentRpcRequestSupportsResponseTimeout, documentRpcEnabled, mapDocumentLifecycleResult, mapDocumentOpenResult, supportsDocumentRpc, } from './document-rpc.js';
6
6
  const HOST_PROTOCOL_VERSION = '1.0';
7
7
  const CLI_HOST_REQUIRED_FEATURES = ['cli.invoke', 'host.shutdown'];
8
8
  const DOCUMENT_HOST_REQUIRED_FEATURES = [...DOCUMENT_RPC_FEATURES, 'host.shutdown'];
@@ -20,6 +20,21 @@ const HOST_DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
20
20
  // abort. The buffer absorbs JSON-RPC serialization, stdio drain, and event-
21
21
  // loop latency.
22
22
  const WATCHDOG_HEADROOM_MS = 5_000;
23
+ export function mapCliInvocationResult(operation, value) {
24
+ const envelopeKey = operation.responseEnvelopeKey;
25
+ if (envelopeKey === null || envelopeKey === undefined)
26
+ return value;
27
+ if (typeof envelopeKey !== 'string' || envelopeKey.length === 0) {
28
+ throw new SuperDocCliError('Generated operation has invalid response envelope metadata.', {
29
+ code: 'HOST_PROTOCOL_ERROR',
30
+ details: { operationId: operation.operationId, responseEnvelopeKey: envelopeKey },
31
+ });
32
+ }
33
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
34
+ return value;
35
+ const extracted = value[envelopeKey];
36
+ return extracted === undefined ? value : extracted;
37
+ }
23
38
  /**
24
39
  * Builds the argv passed to `spawn` for `superdoc host --stdio`. Propagates
25
40
  * `requestTimeoutMs` to the host via `--request-timeout-ms`, since the SDK
@@ -80,9 +95,15 @@ export class HostTransport {
80
95
  pending = new Map();
81
96
  nextRequestId = 1;
82
97
  connecting = null;
98
+ disposePromise = null;
99
+ terminalDisposalError = null;
83
100
  stopping = false;
101
+ activeCalls = new Set();
84
102
  hostFeatures = new Set();
85
103
  documentRpcSessions = new Set();
104
+ dirtyDocumentRpcSessions = new Set();
105
+ documentRpcMutationStates = new Map();
106
+ indeterminateDiscardCloses = new Set();
86
107
  constructor(options) {
87
108
  this.hostBin = options.hostBin;
88
109
  this.processMode = options.processMode ?? 'cli';
@@ -98,22 +119,48 @@ export class HostTransport {
98
119
  details: { defaultChangeMode: options.defaultChangeMode },
99
120
  });
100
121
  }
101
- this.defaultChangeMode = options.defaultChangeMode;
122
+ this.defaultChangeMode = options.defaultChangeMode ?? undefined;
102
123
  this.user = options.user;
103
- this.documentRpcEnabled = documentRpcEnabled(options.env);
104
- if (this.processMode === 'document' && !this.documentRpcEnabled) {
105
- throw new SuperDocCliError('SUPERDOC_SDK_DOCUMENT_HOST_BIN requires SUPERDOC_SDK_DOCUMENT_RPC=1.', {
106
- code: 'INVALID_ARGUMENT',
107
- });
108
- }
124
+ this.documentRpcEnabled = this.processMode === 'document' || documentRpcEnabled(options.env);
109
125
  }
110
126
  async connect() {
111
- await this.ensureConnected();
127
+ await this.runWhileActive(() => this.ensureConnected());
112
128
  }
113
129
  async dispose() {
114
- if (!this.child)
130
+ if (this.disposePromise)
131
+ return this.disposePromise;
132
+ if (this.terminalDisposalError)
133
+ throw this.terminalDisposalError;
134
+ if (!this.child && !this.connecting && this.activeCalls.size === 0)
115
135
  return;
116
136
  this.stopping = true;
137
+ const disposal = this.disposeAfterActiveCalls();
138
+ this.disposePromise = disposal;
139
+ try {
140
+ await disposal;
141
+ }
142
+ finally {
143
+ if (this.disposePromise === disposal)
144
+ this.disposePromise = null;
145
+ this.stopping = false;
146
+ }
147
+ }
148
+ async disposeAfterActiveCalls() {
149
+ await Promise.allSettled(Array.from(this.activeCalls));
150
+ if (this.terminalDisposalError)
151
+ throw this.terminalDisposalError;
152
+ if (!this.child)
153
+ return;
154
+ try {
155
+ await this.reconcileIndeterminateDiscardCloses();
156
+ await this.persistDirtyDocumentRpcSessions();
157
+ }
158
+ catch (error) {
159
+ if (!this.child && error instanceof SuperDocCliError) {
160
+ this.terminalDisposalError = error;
161
+ }
162
+ throw error;
163
+ }
117
164
  const child = this.child;
118
165
  try {
119
166
  await this.sendJsonRpcRequest('host.shutdown', {}, this.shutdownTimeoutMs);
@@ -132,29 +179,80 @@ export class HostTransport {
132
179
  });
133
180
  });
134
181
  this.cleanupProcess(null);
135
- this.stopping = false;
136
182
  }
137
183
  async invoke(operation, params = {}, options = {}) {
184
+ return this.runWhileActive(() => this.invokeWhileActive(operation, params, options));
185
+ }
186
+ async invokeWhileActive(operation, params, options) {
138
187
  await this.ensureConnected();
188
+ const shouldOpenThroughCliTimeoutFallback = this.processMode === 'cli' &&
189
+ (this.requestTimeoutMs !== undefined ||
190
+ (options.timeoutMs !== undefined && !this.hostFeatures.has(DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE)));
139
191
  if (this.documentRpcEnabled &&
140
192
  supportsDocumentRpc(this.hostFeatures) &&
141
- this.defaultChangeMode === undefined &&
142
- this.user === undefined &&
193
+ (this.defaultChangeMode === undefined || this.hostFeatures.has(DOCUMENT_RPC_DEFAULT_CHANGE_MODE_FEATURE)) &&
143
194
  operation.operationId === 'doc.open' &&
144
- canOpenWithDocumentRpc(params, options)) {
145
- const opened = buildDocumentOpenParams(params);
146
- const response = await this.sendJsonRpcRequest('document.open', opened.params, this.resolveWatchdogTimeout(options.timeoutMs));
195
+ canOpenWithDocumentRpc(params, options) &&
196
+ !shouldOpenThroughCliTimeoutFallback) {
197
+ const opened = buildDocumentOpenParams(params, this.user, this.defaultChangeMode);
198
+ const requestTimeoutMs = this.resolveDocumentRequestTimeout(options.timeoutMs);
199
+ const response = await this.sendJsonRpcRequest('document.open', opened.params, this.resolveWatchdogTimeout(options.timeoutMs), { requestTimeoutMs });
147
200
  const result = mapDocumentOpenResult(response, opened.sessionId, params.doc);
148
201
  this.documentRpcSessions.add(opened.sessionId);
202
+ this.indeterminateDiscardCloses.delete(opened.sessionId);
149
203
  return result;
150
204
  }
151
205
  const sessionId = typeof params.sessionId === 'string' ? params.sessionId : undefined;
152
206
  if (sessionId !== undefined && this.documentRpcSessions.has(sessionId)) {
153
- const request = buildDocumentInvokeParams(sessionId, operation, params, options);
154
- const response = await this.sendJsonRpcRequest(request.method, request.params, this.resolveWatchdogTimeout(options.timeoutMs));
155
- const result = mapDocumentLifecycleResult(operation.operationId, response, sessionId);
156
- if (operation.operationId === 'doc.close')
207
+ const request = buildDocumentInvokeParams(sessionId, operation, params, options, this.hostFeatures);
208
+ const supportsResponseTimeout = documentRpcRequestSupportsResponseTimeout(operation, request);
209
+ const requestTimeoutMs = supportsResponseTimeout
210
+ ? this.resolveDocumentRequestTimeout(options.timeoutMs)
211
+ : undefined;
212
+ const documentOperation = operation;
213
+ const requestOptions = typeof request.params.options === 'object' &&
214
+ request.params.options !== null &&
215
+ !Array.isArray(request.params.options)
216
+ ? request.params.options
217
+ : undefined;
218
+ const tracksMutation = request.method === 'document.invoke' && documentOperation.mutates === true && requestOptions?.dryRun !== true;
219
+ if (tracksMutation)
220
+ this.beginDocumentRpcMutation(sessionId);
221
+ let result;
222
+ try {
223
+ const response = await this.sendJsonRpcRequest(request.method, request.params, this.resolveWatchdogTimeout(options.timeoutMs), { requestTimeoutMs });
224
+ result = mapDocumentLifecycleResult(operation.operationId, response, sessionId, operation.operationId === 'doc.save' ? request.params.path === undefined : undefined);
225
+ }
226
+ catch (error) {
227
+ if (tracksMutation) {
228
+ const indeterminate = error instanceof SuperDocCliError && ['TIMEOUT', 'HOST_TIMEOUT', 'HOST_DISCONNECTED'].includes(error.code);
229
+ this.settleDocumentRpcMutation(sessionId, indeterminate ? 'indeterminate' : 'not-applied');
230
+ }
231
+ if (operation.operationId === 'doc.close' &&
232
+ request.params.discard === true &&
233
+ error instanceof SuperDocCliError &&
234
+ error.code === 'TIMEOUT') {
235
+ this.indeterminateDiscardCloses.add(sessionId);
236
+ }
237
+ throw error;
238
+ }
239
+ if (tracksMutation) {
240
+ const failedReceipt = typeof result === 'object' &&
241
+ result !== null &&
242
+ !Array.isArray(result) &&
243
+ result.success === false;
244
+ this.settleDocumentRpcMutation(sessionId, failedReceipt ? 'not-applied' : 'applied');
245
+ }
246
+ if (operation.operationId === 'doc.close') {
157
247
  this.documentRpcSessions.delete(sessionId);
248
+ this.clearDirtyDocumentRpcSession(sessionId);
249
+ this.indeterminateDiscardCloses.delete(sessionId);
250
+ }
251
+ else if (operation.operationId === 'doc.save') {
252
+ if (request.params.mode === undefined || request.params.mode === 'review-preserving') {
253
+ this.markDocumentRpcSessionSaved(sessionId);
254
+ }
255
+ }
158
256
  return result;
159
257
  }
160
258
  if (this.processMode === 'document') {
@@ -163,6 +261,18 @@ export class HostTransport {
163
261
  details: { operationId: operation.operationId },
164
262
  });
165
263
  }
264
+ const cliHostRequestTimeoutMs = this.requestTimeoutMs ?? HOST_DEFAULT_REQUEST_TIMEOUT_MS;
265
+ if (options.timeoutMs !== undefined && options.timeoutMs > cliHostRequestTimeoutMs) {
266
+ throw new SuperDocCliError(`CLI host cannot honor timeoutMs=${options.timeoutMs} above its ${cliHostRequestTimeoutMs}ms request ceiling.`, {
267
+ code: 'DOCUMENT_RPC_INPUT_UNSUPPORTED',
268
+ details: {
269
+ feature: DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE,
270
+ operationId: operation.operationId,
271
+ timeoutMs: options.timeoutMs,
272
+ hostRequestTimeoutMs: cliHostRequestTimeoutMs,
273
+ },
274
+ });
275
+ }
166
276
  const argv = buildOperationArgv(operation, params, options, this.requestTimeoutMs, this.defaultChangeMode, this.user);
167
277
  const stdinBase64 = options.stdinBytes ? Buffer.from(options.stdinBytes).toString('base64') : '';
168
278
  const watchdogTimeout = this.resolveWatchdogTimeout(options.timeoutMs);
@@ -177,7 +287,21 @@ export class HostTransport {
177
287
  });
178
288
  }
179
289
  const resultRecord = response;
180
- return resultRecord.data;
290
+ return mapCliInvocationResult(operation, resultRecord.data);
291
+ }
292
+ runWhileActive(start) {
293
+ if (this.terminalDisposalError) {
294
+ return Promise.reject(this.terminalDisposalError);
295
+ }
296
+ if (this.stopping) {
297
+ return Promise.reject(new SuperDocCliError('Host is disposing.', {
298
+ code: 'HOST_DISCONNECTED',
299
+ }));
300
+ }
301
+ const activeCall = start();
302
+ this.activeCalls.add(activeCall);
303
+ void activeCall.then(() => this.activeCalls.delete(activeCall), () => this.activeCalls.delete(activeCall));
304
+ return activeCall;
181
305
  }
182
306
  async ensureConnected() {
183
307
  if (this.connecting) {
@@ -203,7 +327,7 @@ export class HostTransport {
203
327
  const child = spawn(command, args, {
204
328
  env: {
205
329
  ...process.env,
206
- ...(this.env ?? {}),
330
+ ...this.env,
207
331
  },
208
332
  stdio: ['pipe', 'pipe', 'pipe'],
209
333
  });
@@ -231,7 +355,7 @@ export class HostTransport {
231
355
  }));
232
356
  });
233
357
  child.on('close', (code, signal) => {
234
- if (this.stopping) {
358
+ if (this.stopping && this.dirtyDocumentRpcSessions.size === 0) {
235
359
  this.cleanupProcess(null);
236
360
  return;
237
361
  }
@@ -282,7 +406,13 @@ export class HostTransport {
282
406
  details: { features },
283
407
  });
284
408
  }
285
- const requiredFeatures = this.processMode === 'document' ? DOCUMENT_HOST_REQUIRED_FEATURES : CLI_HOST_REQUIRED_FEATURES;
409
+ const requiredFeatures = this.processMode === 'document'
410
+ ? [
411
+ ...DOCUMENT_HOST_REQUIRED_FEATURES,
412
+ ...(this.defaultChangeMode === undefined ? [] : [DOCUMENT_RPC_DEFAULT_CHANGE_MODE_FEATURE]),
413
+ ...(this.requestTimeoutMs === undefined ? [] : [DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE]),
414
+ ]
415
+ : CLI_HOST_REQUIRED_FEATURES;
286
416
  for (const requiredFeature of requiredFeatures) {
287
417
  if (!features.includes(requiredFeature)) {
288
418
  throw new SuperDocCliError(`Host does not support required feature: ${requiredFeature}`, {
@@ -298,7 +428,79 @@ export class HostTransport {
298
428
  resolveWatchdogTimeout(timeoutMsOverride) {
299
429
  return resolveJsWatchdogTimeout(this.watchdogTimeoutMs, this.requestTimeoutMs, timeoutMsOverride);
300
430
  }
301
- async sendJsonRpcRequest(method, params, watchdogTimeoutMs) {
431
+ resolveDocumentRequestTimeout(timeoutMsOverride) {
432
+ const requestTimeoutMs = timeoutMsOverride ?? this.requestTimeoutMs;
433
+ if (requestTimeoutMs === undefined)
434
+ return undefined;
435
+ if (this.hostFeatures.has(DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE))
436
+ return requestTimeoutMs;
437
+ if (this.processMode === 'cli' && timeoutMsOverride === undefined)
438
+ return undefined;
439
+ throw new SuperDocCliError(`Structured document RPC host does not support ${DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE}.`, {
440
+ code: 'DOCUMENT_RPC_INPUT_UNSUPPORTED',
441
+ details: { feature: DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE },
442
+ });
443
+ }
444
+ beginDocumentRpcMutation(sessionId) {
445
+ const state = this.documentRpcMutationStates.get(sessionId) ?? {
446
+ confirmedDirty: this.dirtyDocumentRpcSessions.has(sessionId),
447
+ pendingCount: 0,
448
+ };
449
+ state.pendingCount += 1;
450
+ this.documentRpcMutationStates.set(sessionId, state);
451
+ this.dirtyDocumentRpcSessions.add(sessionId);
452
+ }
453
+ settleDocumentRpcMutation(sessionId, outcome) {
454
+ const state = this.documentRpcMutationStates.get(sessionId);
455
+ if (!state)
456
+ return;
457
+ state.pendingCount = Math.max(0, state.pendingCount - 1);
458
+ if (outcome !== 'not-applied')
459
+ state.confirmedDirty = true;
460
+ if (state.confirmedDirty || state.pendingCount > 0) {
461
+ this.dirtyDocumentRpcSessions.add(sessionId);
462
+ return;
463
+ }
464
+ this.clearDirtyDocumentRpcSession(sessionId);
465
+ }
466
+ clearDirtyDocumentRpcSession(sessionId) {
467
+ this.dirtyDocumentRpcSessions.delete(sessionId);
468
+ this.documentRpcMutationStates.delete(sessionId);
469
+ }
470
+ markDocumentRpcSessionSaved(sessionId) {
471
+ const state = this.documentRpcMutationStates.get(sessionId);
472
+ if (!state || state.pendingCount === 0) {
473
+ this.clearDirtyDocumentRpcSession(sessionId);
474
+ return;
475
+ }
476
+ state.confirmedDirty = false;
477
+ this.dirtyDocumentRpcSessions.add(sessionId);
478
+ }
479
+ async persistDirtyDocumentRpcSessions() {
480
+ if (this.dirtyDocumentRpcSessions.size === 0)
481
+ return;
482
+ if (!this.hostFeatures.has(DOCUMENT_RPC_SOURCE_SAVE_FEATURE)) {
483
+ throw new SuperDocCliError('Document host cannot persist dirty sessions during client disposal because it does not support source saves.', {
484
+ code: 'DOCUMENT_RPC_INPUT_UNSUPPORTED',
485
+ details: { feature: DOCUMENT_RPC_SOURCE_SAVE_FEATURE },
486
+ });
487
+ }
488
+ for (const sessionId of this.dirtyDocumentRpcSessions) {
489
+ const response = await this.sendJsonRpcRequest('document.save', { sessionId }, this.resolveWatchdogTimeout(undefined));
490
+ mapDocumentLifecycleResult('doc.save', response, sessionId, true);
491
+ this.markDocumentRpcSessionSaved(sessionId);
492
+ }
493
+ }
494
+ async reconcileIndeterminateDiscardCloses() {
495
+ for (const sessionId of this.indeterminateDiscardCloses) {
496
+ const response = await this.sendJsonRpcRequest('document.close', { sessionId, discard: true }, this.resolveWatchdogTimeout(undefined));
497
+ mapDocumentLifecycleResult('doc.close', response, sessionId);
498
+ this.documentRpcSessions.delete(sessionId);
499
+ this.clearDirtyDocumentRpcSession(sessionId);
500
+ this.indeterminateDiscardCloses.delete(sessionId);
501
+ }
502
+ }
503
+ async sendJsonRpcRequest(method, params, watchdogTimeoutMs, metadata = {}) {
302
504
  const child = this.child;
303
505
  if (!child || !child.stdin.writable) {
304
506
  throw new SuperDocCliError('Host process is not available.', {
@@ -318,6 +520,7 @@ export class HostTransport {
318
520
  id,
319
521
  method,
320
522
  params,
523
+ ...(metadata.requestTimeoutMs === undefined ? {} : { requestTimeoutMs: metadata.requestTimeoutMs }),
321
524
  });
322
525
  const promise = new Promise((resolve, reject) => {
323
526
  const timer = setTimeout(() => {
@@ -428,8 +631,17 @@ export class HostTransport {
428
631
  this.cleanupProcess(error);
429
632
  }
430
633
  cleanupProcess(error) {
634
+ if (error && this.dirtyDocumentRpcSessions.size > 0 && !this.terminalDisposalError) {
635
+ this.terminalDisposalError = new SuperDocCliError('Document host disconnected before dirty sessions could be persisted.', {
636
+ code: 'HOST_DISCONNECTED',
637
+ details: { causeCode: error.code, causeDetails: error.details },
638
+ });
639
+ }
431
640
  this.hostFeatures.clear();
432
641
  this.documentRpcSessions.clear();
642
+ this.dirtyDocumentRpcSessions.clear();
643
+ this.documentRpcMutationStates.clear();
644
+ this.indeterminateDiscardCloses.clear();
433
645
  const child = this.child;
434
646
  if (child) {
435
647
  child.removeAllListeners();
@@ -2,9 +2,36 @@
2
2
 
3
3
  var host = require('./host.cjs');
4
4
  var embeddedCli = require('./embedded-cli.cjs');
5
+ var embeddedDocumentHost = require('./embedded-document-host.cjs');
5
6
  var debugTrace = require('./debug-trace.cjs');
6
7
  var errors = require('./errors.cjs');
7
8
 
9
+ function resolveRuntimeProcess(options = {}, embedded = {
10
+ cli: embeddedCli.resolveEmbeddedCliBinary,
11
+ documentHost: embeddedDocumentHost.resolveEmbeddedDocumentHostBinary,
12
+ }) {
13
+ const configuredDocumentHostPath = options.documentHostPath;
14
+ if (configuredDocumentHostPath !== undefined && configuredDocumentHostPath.trim().length === 0) {
15
+ throw new errors.SuperDocCliError('documentHostPath must be a non-empty path.', {
16
+ code: 'INVALID_ARGUMENT',
17
+ });
18
+ }
19
+ const configuredDocumentHostBin = options.env?.SUPERDOC_SDK_DOCUMENT_HOST_BIN ?? process.env.SUPERDOC_SDK_DOCUMENT_HOST_BIN;
20
+ const documentHostBin = configuredDocumentHostPath ??
21
+ (configuredDocumentHostBin === 'embedded' ? embedded.documentHost() : configuredDocumentHostBin);
22
+ if (documentHostBin !== undefined && documentHostBin.trim().length === 0) {
23
+ throw new errors.SuperDocCliError('SUPERDOC_SDK_DOCUMENT_HOST_BIN must be a non-empty path.', {
24
+ code: 'INVALID_ARGUMENT',
25
+ });
26
+ }
27
+ if (documentHostBin !== undefined) {
28
+ return { hostBin: documentHostBin, processMode: 'document' };
29
+ }
30
+ return {
31
+ hostBin: options.env?.SUPERDOC_CLI_BIN ?? process.env.SUPERDOC_CLI_BIN ?? embedded.cli(),
32
+ processMode: 'cli',
33
+ };
34
+ }
8
35
  /**
9
36
  * Internal runtime that delegates operations to a persistent host transport.
10
37
  *
@@ -14,17 +41,10 @@ var errors = require('./errors.cjs');
14
41
  class SuperDocRuntime {
15
42
  transport;
16
43
  constructor(options = {}) {
17
- const documentHostBin = options.env?.SUPERDOC_SDK_DOCUMENT_HOST_BIN ?? process.env.SUPERDOC_SDK_DOCUMENT_HOST_BIN;
18
- if (documentHostBin !== undefined && documentHostBin.trim().length === 0) {
19
- throw new errors.SuperDocCliError('SUPERDOC_SDK_DOCUMENT_HOST_BIN must be a non-empty path.', {
20
- code: 'INVALID_ARGUMENT',
21
- });
22
- }
23
- const hostBin = documentHostBin ?? options.env?.SUPERDOC_CLI_BIN ?? process.env.SUPERDOC_CLI_BIN ?? embeddedCli.resolveEmbeddedCliBinary();
44
+ const runtimeProcess = resolveRuntimeProcess(options);
24
45
  this.transport = new host.HostTransport({
25
46
  ...options,
26
- hostBin,
27
- processMode: documentHostBin === undefined ? 'cli' : 'document',
47
+ ...runtimeProcess,
28
48
  });
29
49
  }
30
50
  async connect() {
@@ -67,3 +87,4 @@ class SuperDocRuntime {
67
87
  }
68
88
 
69
89
  exports.SuperDocRuntime = SuperDocRuntime;
90
+ exports.resolveRuntimeProcess = resolveRuntimeProcess;
@@ -1,4 +1,13 @@
1
1
  import type { DocumentRuntimeKind, InvokeOptions, OperationParamSpec, OperationSpec, RuntimeInvoker, SuperDocClientOptions } from './transport-common.js';
2
+ type RuntimeProcessResolution = {
3
+ hostBin: string;
4
+ processMode: 'cli' | 'document';
5
+ };
6
+ type EmbeddedRuntimeResolvers = {
7
+ cli: () => string;
8
+ documentHost: () => string;
9
+ };
10
+ export declare function resolveRuntimeProcess(options?: SuperDocClientOptions, embedded?: EmbeddedRuntimeResolvers): RuntimeProcessResolution;
2
11
  /**
3
12
  * Internal runtime that delegates operations to a persistent host transport.
4
13
  *
@@ -1,7 +1,34 @@
1
1
  import { HostTransport } from './host.js';
2
2
  import { resolveEmbeddedCliBinary } from './embedded-cli.js';
3
+ import { resolveEmbeddedDocumentHostBinary } from './embedded-document-host.js';
3
4
  import { writeSdkDebugTrace } from './debug-trace.js';
4
5
  import { SuperDocCliError } from './errors.js';
6
+ export function resolveRuntimeProcess(options = {}, embedded = {
7
+ cli: resolveEmbeddedCliBinary,
8
+ documentHost: resolveEmbeddedDocumentHostBinary,
9
+ }) {
10
+ const configuredDocumentHostPath = options.documentHostPath;
11
+ if (configuredDocumentHostPath !== undefined && configuredDocumentHostPath.trim().length === 0) {
12
+ throw new SuperDocCliError('documentHostPath must be a non-empty path.', {
13
+ code: 'INVALID_ARGUMENT',
14
+ });
15
+ }
16
+ const configuredDocumentHostBin = options.env?.SUPERDOC_SDK_DOCUMENT_HOST_BIN ?? process.env.SUPERDOC_SDK_DOCUMENT_HOST_BIN;
17
+ const documentHostBin = configuredDocumentHostPath ??
18
+ (configuredDocumentHostBin === 'embedded' ? embedded.documentHost() : configuredDocumentHostBin);
19
+ if (documentHostBin !== undefined && documentHostBin.trim().length === 0) {
20
+ throw new SuperDocCliError('SUPERDOC_SDK_DOCUMENT_HOST_BIN must be a non-empty path.', {
21
+ code: 'INVALID_ARGUMENT',
22
+ });
23
+ }
24
+ if (documentHostBin !== undefined) {
25
+ return { hostBin: documentHostBin, processMode: 'document' };
26
+ }
27
+ return {
28
+ hostBin: options.env?.SUPERDOC_CLI_BIN ?? process.env.SUPERDOC_CLI_BIN ?? embedded.cli(),
29
+ processMode: 'cli',
30
+ };
31
+ }
5
32
  /**
6
33
  * Internal runtime that delegates operations to a persistent host transport.
7
34
  *
@@ -11,17 +38,10 @@ import { SuperDocCliError } from './errors.js';
11
38
  export class SuperDocRuntime {
12
39
  transport;
13
40
  constructor(options = {}) {
14
- const documentHostBin = options.env?.SUPERDOC_SDK_DOCUMENT_HOST_BIN ?? process.env.SUPERDOC_SDK_DOCUMENT_HOST_BIN;
15
- if (documentHostBin !== undefined && documentHostBin.trim().length === 0) {
16
- throw new SuperDocCliError('SUPERDOC_SDK_DOCUMENT_HOST_BIN must be a non-empty path.', {
17
- code: 'INVALID_ARGUMENT',
18
- });
19
- }
20
- const hostBin = documentHostBin ?? options.env?.SUPERDOC_CLI_BIN ?? process.env.SUPERDOC_CLI_BIN ?? resolveEmbeddedCliBinary();
41
+ const runtimeProcess = resolveRuntimeProcess(options);
21
42
  this.transport = new HostTransport({
22
43
  ...options,
23
- hostBin,
24
- processMode: documentHostBin === undefined ? 'cli' : 'document',
44
+ ...runtimeProcess,
25
45
  });
26
46
  }
27
47
  async connect() {
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ // AUTO-GENERATED by scripts/embed-version.mjs — DO NOT EDIT.
4
+ // Source of truth: package.json. Regenerated on every SDK build so the
5
+ // SDK retains its own version identity when bundled into another package.
6
+ const SDK_VERSION = '2.8.0';
7
+
8
+ exports.SDK_VERSION = SDK_VERSION;
@@ -0,0 +1 @@
1
+ export declare const SDK_VERSION = "2.8.0";
@@ -0,0 +1,4 @@
1
+ // AUTO-GENERATED by scripts/embed-version.mjs — DO NOT EDIT.
2
+ // Source of truth: package.json. Regenerated on every SDK build so the
3
+ // SDK retains its own version identity when bundled into another package.
4
+ export const SDK_VERSION = '2.8.0';
@@ -163,5 +163,6 @@ function buildOperationArgv(operation, params, options, runtimeTimeoutMs, defaul
163
163
  return argv;
164
164
  }
165
165
 
166
+ exports.applyOperationParamAliases = applyOperationParamAliases;
166
167
  exports.buildOperationArgv = buildOperationArgv;
167
168
  exports.resolveInvocation = resolveInvocation;
@@ -34,6 +34,14 @@ export interface UserIdentity {
34
34
  }
35
35
  export interface SuperDocClientOptions {
36
36
  env?: Record<string, string | undefined>;
37
+ /**
38
+ * Standalone document-host entry to use instead of the embedded CLI.
39
+ *
40
+ * The host-only path supports local lifecycle methods and generated Document
41
+ * API operations through structured RPC. Interface-only conveniences fail
42
+ * without CLI fallback.
43
+ */
44
+ documentHostPath?: string;
37
45
  /**
38
46
  * Default document engine for `client.open(...)`. Explicit `runtime` on an
39
47
  * individual open call takes precedence.
@@ -42,11 +50,13 @@ export interface SuperDocClientOptions {
42
50
  startupTimeoutMs?: number;
43
51
  shutdownTimeoutMs?: number;
44
52
  /**
45
- * Upper bound (ms) on how long the host process may spend on a single
46
- * `cli.invoke` request before it kills the operation and returns a
47
- * `RequestTimeout` error. Propagated to the host via `--request-timeout-ms`
48
- * at spawn. Raise this for documents that legitimately need more than 30s
49
- * to process; the SDK widens its own JSON-RPC watchdog to match.
53
+ * Upper bound (ms) on how long the host process may spend on a request
54
+ * before it returns a `RequestTimeout` error. Retry-safe structured document
55
+ * requests carry this budget as transport metadata; requests that may apply
56
+ * non-idempotent side effects await a definitive result instead. CLI-host
57
+ * requests also receive the budget through `--request-timeout-ms` at spawn.
58
+ * Raise this for documents that legitimately need more than 30s to process;
59
+ * the SDK widens its own JSON-RPC watchdog to match.
50
60
  *
51
61
  * Defaults to the host's own default (30s) when unset.
52
62
  */
@@ -54,11 +64,13 @@ export interface SuperDocClientOptions {
54
64
  /**
55
65
  * JS-side watchdog (ms) the SDK waits for a host reply before giving up.
56
66
  * Independent of {@link requestTimeoutMs} (which controls the host-side
57
- * operation budget). Most callers should leave this at its default and use
58
- * {@link requestTimeoutMs} as the single operation-timeout knob
59
- * `resolveWatchdogTimeout` already widens the watchdog above the host
60
- * ceiling automatically. Override only when you need to detect a hung or
61
- * crashed host faster than the operation budget allows.
67
+ * operation budget). Structured requests that may apply non-idempotent side
68
+ * effects wait for a definitive host result instead of applying an unsafe
69
+ * response deadline. Most callers should leave this at its default and use
70
+ * {@link requestTimeoutMs} as the single timeout knob for retry-safe work
71
+ * `resolveWatchdogTimeout` already widens the watchdog above the host ceiling
72
+ * automatically. Override only when you need to detect a hung or crashed host
73
+ * faster than the operation budget allows.
62
74
  */
63
75
  watchdogTimeoutMs?: number;
64
76
  maxQueueDepth?: number;
@@ -70,6 +82,12 @@ export interface CliInvocation {
70
82
  prefixArgs: string[];
71
83
  }
72
84
  export declare function resolveInvocation(cliBin: string): CliInvocation;
85
+ /**
86
+ * Resolve contract-shaped param aliases onto their canonical generated names.
87
+ * Returns the same object when no alias applies. Throws on a conflicting
88
+ * canonical/alias pair so divergent values can never be silently coalesced.
89
+ */
90
+ export declare function applyOperationParamAliases(operation: OperationSpec, params: Record<string, unknown>): Record<string, unknown>;
73
91
  /**
74
92
  * Build the CLI argument vector for an operation invocation.
75
93
  *
@@ -54,7 +54,7 @@ function assertKnownTemplatesApplyParams(operation, params) {
54
54
  * Returns the same object when no alias applies. Throws on a conflicting
55
55
  * canonical/alias pair so divergent values can never be silently coalesced.
56
56
  */
57
- function applyOperationParamAliases(operation, params) {
57
+ export function applyOperationParamAliases(operation, params) {
58
58
  const aliasMap = OPERATION_PARAM_ALIASES[operation.operationId];
59
59
  if (!aliasMap)
60
60
  return params;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superdoc/sdk",
3
- "version": "2.6.0",
3
+ "version": "2.8.0",
4
4
  "description": "Node SDK for SuperDoc, wrapping the SuperDoc CLI to read and edit .docx files from JavaScript and TypeScript.",
5
5
  "private": false,
6
6
  "license": "AGPL-3.0",
@@ -37,17 +37,17 @@
37
37
  "typescript": "^5.9.2"
38
38
  },
39
39
  "optionalDependencies": {
40
- "@superdoc/sdk-darwin-arm64": "2.6.0",
41
- "@superdoc/sdk-linux-x64": "2.6.0",
42
- "@superdoc/sdk-darwin-x64": "2.6.0",
43
- "@superdoc/sdk-linux-arm64": "2.6.0",
44
- "@superdoc/sdk-windows-x64": "2.6.0"
40
+ "@superdoc/sdk-darwin-arm64": "2.8.0",
41
+ "@superdoc/sdk-darwin-x64": "2.8.0",
42
+ "@superdoc/sdk-linux-x64": "2.8.0",
43
+ "@superdoc/sdk-linux-arm64": "2.8.0",
44
+ "@superdoc/sdk-windows-x64": "2.8.0"
45
45
  },
46
46
  "publishConfig": {
47
47
  "access": "public"
48
48
  },
49
49
  "scripts": {
50
- "build": "rm -rf dist && node scripts/embed-prompts.mjs && node scripts/embed-tools.mjs && tsc && rollup -c rollup.cjs.config.mjs && rm -rf dist/prompts && mkdir -p dist/prompts && cp src/prompts/*.md dist/prompts/ && pnpm run audit:publish",
50
+ "build": "rm -rf dist && node scripts/embed-version.mjs && node scripts/embed-prompts.mjs && node scripts/embed-tools.mjs && tsc && rollup -c rollup.cjs.config.mjs && rm -rf dist/prompts && mkdir -p dist/prompts && cp src/prompts/*.md dist/prompts/ && pnpm run audit:publish",
51
51
  "audit:publish": "node ../../../../scripts/audit-publish-artifact.mjs dist --label sdk-node-dist",
52
52
  "typecheck": "tsc --noEmit",
53
53
  "test:document-host": "bun test src/runtime/__tests__/host-spawn-args.test.ts src/runtime/__tests__/document-rpc.test.ts src/__tests__/structured-document-rpc.e2e.test.ts src/__tests__/request-timeout-ms.e2e.test.ts",
@@ -43,5 +43,5 @@
43
43
  "mutates": true
44
44
  }
45
45
  ],
46
- "contractHash": "5967b3cb6c6ca5f65dd887d1c3b2908478e47ac6a2b76f7b2bb207f3b5b21324"
46
+ "contractHash": "570f35bff614f42af4610ad37ad4d21d776cdf8e3515c206d25d50c0102d7484"
47
47
  }