@superdoc/sdk 2.10.0-next.4 → 2.10.0-next.5

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.
@@ -21,6 +21,7 @@ const CONTRACT = {
21
21
  "document.open",
22
22
  "document.open.defaultChangeMode",
23
23
  "document.currentRevision",
24
+ "document.replaceFile",
24
25
  "document.invoke",
25
26
  "document.save",
26
27
  "document.save.source",
@@ -2905,6 +2905,7 @@ export const CONTRACT = {
2905
2905
  "document.open",
2906
2906
  "document.open.defaultChangeMode",
2907
2907
  "document.currentRevision",
2908
+ "document.replaceFile",
2908
2909
  "document.invoke",
2909
2910
  "document.save",
2910
2911
  "document.save.source",
@@ -2936,6 +2937,7 @@ export const CONTRACT = {
2936
2937
  "document.open",
2937
2938
  "document.open.defaultChangeMode",
2938
2939
  "document.currentRevision",
2940
+ "document.replaceFile",
2939
2941
  "document.invoke",
2940
2942
  "document.save",
2941
2943
  "document.save.source",
package/dist/index.cjs CHANGED
@@ -37,6 +37,15 @@ class BoundRuntime {
37
37
  }
38
38
  return this.runtime.invoke(operation, { ...params, sessionId: this.sessionId }, options);
39
39
  }
40
+ async replaceFile(source, options = {}) {
41
+ if (this.closed) {
42
+ throw new errors.SuperDocCliError('Document handle is closed; cannot replace its file.', {
43
+ code: 'DOCUMENT_CLOSED',
44
+ details: { sessionId: this.sessionId },
45
+ });
46
+ }
47
+ return this.runtime.replaceFile(this.sessionId, source, options);
48
+ }
40
49
  markClosed() {
41
50
  this.closed = true;
42
51
  }
@@ -72,6 +81,9 @@ class SuperDocDocumentCore {
72
81
  async save(params = {}, options = {}) {
73
82
  return this.boundRuntime.invoke(contract.CONTRACT.operations['doc.save'], params, options);
74
83
  }
84
+ async replaceFile(source, options = {}) {
85
+ return this.boundRuntime.replaceFile(source, options);
86
+ }
75
87
  async close(params = {}, options = {}) {
76
88
  const result = await this.boundRuntime.invoke(contract.CONTRACT.operations['doc.close'], params, options);
77
89
  this.boundRuntime.markClosed();
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type BoundDocApi, type DocCloseBoundParams, type DocCloseResult, type DocFormatApplyBoundParams, type DocFormatApplyResult, type DocOpenParams as GeneratedDocOpenParams, type DocOpenResult, type DocSaveBoundParams, type DocSaveResult } from './generated/client.js';
2
- import { SuperDocRuntime, type SuperDocClientOptions, type DocOpenOptions, type InvokeOptions, type OperationSpec, type RuntimeInvoker } from './runtime/process.js';
2
+ import { SuperDocRuntime, type SuperDocClientOptions, type DocOpenOptions, type InvokeOptions, type OperationSpec, type ReplaceFileOptions, type ReplaceFileResult, type ReplaceFileSource, type RuntimeInvoker } from './runtime/process.js';
3
3
  /**
4
4
  * Wraps a raw runtime and injects a fixed sessionId into every invoke call.
5
5
  * Implements RuntimeInvoker so generated code can use it directly.
@@ -12,6 +12,7 @@ declare class BoundRuntime implements RuntimeInvoker {
12
12
  private closed;
13
13
  constructor(runtime: SuperDocRuntime, sessionId: string);
14
14
  invoke<TData = unknown>(operation: OperationSpec, params?: Record<string, unknown>, options?: InvokeOptions): Promise<TData>;
15
+ replaceFile(source: ReplaceFileSource, options?: ReplaceFileOptions): Promise<ReplaceFileResult>;
15
16
  markClosed(): void;
16
17
  }
17
18
  export interface DocFormatRangeBoundParams extends Omit<DocFormatApplyBoundParams, 'inline'> {
@@ -33,6 +34,7 @@ declare class SuperDocDocumentCore {
33
34
  /** Read-only snapshot of the initial doc.open response metadata. */
34
35
  get openResult(): DocOpenResult;
35
36
  save(params?: DocSaveBoundParams, options?: InvokeOptions): Promise<DocSaveResult>;
37
+ replaceFile(source: ReplaceFileSource, options?: ReplaceFileOptions): Promise<ReplaceFileResult>;
36
38
  close(params?: DocCloseBoundParams, options?: InvokeOptions): Promise<DocCloseResult>;
37
39
  /** @internal */
38
40
  markClosed(): void;
@@ -42,6 +44,7 @@ type SuperDocDocumentInstance = SuperDocDocumentCore & BoundDocApi;
42
44
  export declare const SuperDocDocument: new (boundRuntime: BoundRuntime, sessionId: string, openResult: DocOpenResult, client: SuperDocClient) => SuperDocDocumentInstance;
43
45
  export type SuperDocDocument = SuperDocDocumentInstance;
44
46
  export type DocOpenParams = GeneratedDocOpenParams;
47
+ export type { ReplaceFileOptions, ReplaceFileResult, ReplaceFileSource };
45
48
  export interface DocDescribeCommandParams {
46
49
  operationId: string;
47
50
  [key: string]: unknown;
package/dist/index.js CHANGED
@@ -29,6 +29,15 @@ class BoundRuntime {
29
29
  }
30
30
  return this.runtime.invoke(operation, { ...params, sessionId: this.sessionId }, options);
31
31
  }
32
+ async replaceFile(source, options = {}) {
33
+ if (this.closed) {
34
+ throw new SuperDocCliError('Document handle is closed; cannot replace its file.', {
35
+ code: 'DOCUMENT_CLOSED',
36
+ details: { sessionId: this.sessionId },
37
+ });
38
+ }
39
+ return this.runtime.replaceFile(this.sessionId, source, options);
40
+ }
32
41
  markClosed() {
33
42
  this.closed = true;
34
43
  }
@@ -64,6 +73,9 @@ class SuperDocDocumentCore {
64
73
  async save(params = {}, options = {}) {
65
74
  return this.boundRuntime.invoke(CONTRACT.operations['doc.save'], params, options);
66
75
  }
76
+ async replaceFile(source, options = {}) {
77
+ return this.boundRuntime.replaceFile(source, options);
78
+ }
67
79
  async close(params = {}, options = {}) {
68
80
  const result = await this.boundRuntime.invoke(CONTRACT.operations['doc.close'], params, options);
69
81
  this.boundRuntime.markClosed();
@@ -5,6 +5,7 @@ var errors = require('./errors.cjs');
5
5
  var transportCommon = require('./transport-common.cjs');
6
6
 
7
7
  const DOCUMENT_RPC_FEATURES = ['document.open', 'document.invoke', 'document.save', 'document.close'];
8
+ const DOCUMENT_RPC_REPLACE_FILE_FEATURE = 'document.replaceFile';
8
9
  const DOCUMENT_RPC_DEFAULT_CHANGE_MODE_FEATURE = 'document.open.defaultChangeMode';
9
10
  const DOCUMENT_RPC_SOURCE_SAVE_FEATURE = 'document.save.source';
10
11
  const DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE = 'host.request.timeoutMs';
@@ -103,6 +104,30 @@ function mapDocumentOpenResult(response, sessionId, path) {
103
104
  dirty: false,
104
105
  };
105
106
  }
107
+ function mapDocumentReplaceFileResult(response, sessionId) {
108
+ if (typeof response !== 'object' || response == null || Array.isArray(response)) {
109
+ throw new errors.SuperDocCliError('Host returned invalid document.replaceFile result.', {
110
+ code: 'HOST_PROTOCOL_ERROR',
111
+ details: { result: response },
112
+ });
113
+ }
114
+ const record = response;
115
+ if (record.sessionId !== sessionId ||
116
+ record.replaced !== true ||
117
+ typeof record.byteLength !== 'number' ||
118
+ typeof record.revision !== 'string') {
119
+ throw new errors.SuperDocCliError('Host returned invalid document.replaceFile session metadata.', {
120
+ code: 'HOST_PROTOCOL_ERROR',
121
+ details: { expectedSessionId: sessionId, result: response },
122
+ });
123
+ }
124
+ return {
125
+ contextId: sessionId,
126
+ runtime: 'v2',
127
+ replaced: true,
128
+ document: { byteLength: record.byteLength, revision: record.revision },
129
+ };
130
+ }
106
131
  function supportsTrackedMode(operation) {
107
132
  return operation.supportsTrackedMode === true || operation.supportsConditionalTrackedMode === true;
108
133
  }
@@ -316,6 +341,7 @@ function mapDocumentLifecycleResult(operationId, response, sessionId, expectedIn
316
341
 
317
342
  exports.DOCUMENT_RPC_DEFAULT_CHANGE_MODE_FEATURE = DOCUMENT_RPC_DEFAULT_CHANGE_MODE_FEATURE;
318
343
  exports.DOCUMENT_RPC_FEATURES = DOCUMENT_RPC_FEATURES;
344
+ exports.DOCUMENT_RPC_REPLACE_FILE_FEATURE = DOCUMENT_RPC_REPLACE_FILE_FEATURE;
319
345
  exports.DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE = DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE;
320
346
  exports.DOCUMENT_RPC_SOURCE_SAVE_FEATURE = DOCUMENT_RPC_SOURCE_SAVE_FEATURE;
321
347
  exports.buildDocumentInvokeParams = buildDocumentInvokeParams;
@@ -325,4 +351,5 @@ exports.documentRpcEnabled = documentRpcEnabled;
325
351
  exports.documentRpcRequestSupportsResponseTimeout = documentRpcRequestSupportsResponseTimeout;
326
352
  exports.mapDocumentLifecycleResult = mapDocumentLifecycleResult;
327
353
  exports.mapDocumentOpenResult = mapDocumentOpenResult;
354
+ exports.mapDocumentReplaceFileResult = mapDocumentReplaceFileResult;
328
355
  exports.supportsDocumentRpc = supportsDocumentRpc;
@@ -1,5 +1,6 @@
1
1
  import { type ChangeMode, type InvokeOptions, type OperationSpec, type UserIdentity } from './transport-common.js';
2
2
  export declare const DOCUMENT_RPC_FEATURES: readonly ["document.open", "document.invoke", "document.save", "document.close"];
3
+ export declare const DOCUMENT_RPC_REPLACE_FILE_FEATURE = "document.replaceFile";
3
4
  export declare const DOCUMENT_RPC_DEFAULT_CHANGE_MODE_FEATURE = "document.open.defaultChangeMode";
4
5
  export declare const DOCUMENT_RPC_SOURCE_SAVE_FEATURE = "document.save.source";
5
6
  export declare const DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE = "host.request.timeoutMs";
@@ -15,6 +16,7 @@ export declare function buildDocumentOpenParams(params: Record<string, unknown>,
15
16
  readonly params: Record<string, unknown>;
16
17
  };
17
18
  export declare function mapDocumentOpenResult(response: unknown, sessionId: string, path: string): Record<string, unknown>;
19
+ export declare function mapDocumentReplaceFileResult(response: unknown, sessionId: string): Record<string, unknown>;
18
20
  export declare function buildDocumentInvokeParams(sessionId: string, operation: OperationSpec, params: Record<string, unknown>, options: InvokeOptions, hostFeatures?: ReadonlySet<string>): {
19
21
  readonly method: string;
20
22
  readonly params: Record<string, unknown>;
@@ -2,6 +2,7 @@ import { randomUUID } from 'node:crypto';
2
2
  import { SuperDocCliError } from './errors.js';
3
3
  import { applyOperationParamAliases, } from './transport-common.js';
4
4
  export const DOCUMENT_RPC_FEATURES = ['document.open', 'document.invoke', 'document.save', 'document.close'];
5
+ export const DOCUMENT_RPC_REPLACE_FILE_FEATURE = 'document.replaceFile';
5
6
  export const DOCUMENT_RPC_DEFAULT_CHANGE_MODE_FEATURE = 'document.open.defaultChangeMode';
6
7
  export const DOCUMENT_RPC_SOURCE_SAVE_FEATURE = 'document.save.source';
7
8
  export const DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE = 'host.request.timeoutMs';
@@ -100,6 +101,30 @@ export function mapDocumentOpenResult(response, sessionId, path) {
100
101
  dirty: false,
101
102
  };
102
103
  }
104
+ export function mapDocumentReplaceFileResult(response, sessionId) {
105
+ if (typeof response !== 'object' || response == null || Array.isArray(response)) {
106
+ throw new SuperDocCliError('Host returned invalid document.replaceFile result.', {
107
+ code: 'HOST_PROTOCOL_ERROR',
108
+ details: { result: response },
109
+ });
110
+ }
111
+ const record = response;
112
+ if (record.sessionId !== sessionId ||
113
+ record.replaced !== true ||
114
+ typeof record.byteLength !== 'number' ||
115
+ typeof record.revision !== 'string') {
116
+ throw new SuperDocCliError('Host returned invalid document.replaceFile session metadata.', {
117
+ code: 'HOST_PROTOCOL_ERROR',
118
+ details: { expectedSessionId: sessionId, result: response },
119
+ });
120
+ }
121
+ return {
122
+ contextId: sessionId,
123
+ runtime: 'v2',
124
+ replaced: true,
125
+ document: { byteLength: record.byteLength, revision: record.revision },
126
+ };
127
+ }
103
128
  function supportsTrackedMode(operation) {
104
129
  return operation.supportsTrackedMode === true || operation.supportsConditionalTrackedMode === true;
105
130
  }
@@ -187,6 +187,34 @@ class HostTransport {
187
187
  async invoke(operation, params = {}, options = {}) {
188
188
  return this.runWhileActive(() => this.invokeWhileActive(operation, params, options));
189
189
  }
190
+ async replaceFile(sessionId, path, options = {}) {
191
+ return this.runWhileActive(async () => {
192
+ await this.ensureConnected();
193
+ if (!this.hostFeatures.has(documentRpc.DOCUMENT_RPC_REPLACE_FILE_FEATURE)) {
194
+ throw new errors.SuperDocCliError('The connected host does not support document replacement.', {
195
+ code: 'CAPABILITY_UNSUPPORTED',
196
+ details: { feature: documentRpc.DOCUMENT_RPC_REPLACE_FILE_FEATURE },
197
+ });
198
+ }
199
+ const tracksDirectSession = this.documentRpcSessions.has(sessionId);
200
+ if (tracksDirectSession)
201
+ this.beginDocumentRpcMutation(sessionId);
202
+ try {
203
+ const response = await this.sendJsonRpcRequest(documentRpc.DOCUMENT_RPC_REPLACE_FILE_FEATURE, { sessionId, path }, this.resolveWatchdogTimeout(options.timeoutMs));
204
+ const result = documentRpc.mapDocumentReplaceFileResult(response, sessionId);
205
+ if (tracksDirectSession)
206
+ this.settleDocumentRpcMutation(sessionId, 'applied');
207
+ return result;
208
+ }
209
+ catch (error) {
210
+ if (tracksDirectSession) {
211
+ const indeterminate = error instanceof errors.SuperDocCliError && ['TIMEOUT', 'HOST_TIMEOUT', 'HOST_DISCONNECTED'].includes(error.code);
212
+ this.settleDocumentRpcMutation(sessionId, indeterminate ? 'indeterminate' : 'not-applied');
213
+ }
214
+ throw error;
215
+ }
216
+ });
217
+ }
190
218
  async invokeWhileActive(operation, params, options) {
191
219
  const collaborationAuth = options.collaborationAuth === undefined ? undefined : transportCommon.normalizeCollaborationAuth(options.collaborationAuth);
192
220
  if (collaborationAuth !== undefined) {
@@ -285,8 +313,11 @@ class HostTransport {
285
313
  details: { operationId: operation.operationId },
286
314
  });
287
315
  }
316
+ const supportsRequestTimeoutMetadata = this.hostFeatures.has(documentRpc.DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE);
288
317
  const cliHostRequestTimeoutMs = this.requestTimeoutMs ?? HOST_DEFAULT_REQUEST_TIMEOUT_MS;
289
- if (options.timeoutMs !== undefined && options.timeoutMs > cliHostRequestTimeoutMs) {
318
+ if (!supportsRequestTimeoutMetadata &&
319
+ options.timeoutMs !== undefined &&
320
+ options.timeoutMs > cliHostRequestTimeoutMs) {
290
321
  throw new errors.SuperDocCliError(`CLI host cannot honor timeoutMs=${options.timeoutMs} above its ${cliHostRequestTimeoutMs}ms request ceiling.`, {
291
322
  code: 'DOCUMENT_RPC_INPUT_UNSUPPORTED',
292
323
  details: {
@@ -304,7 +335,9 @@ class HostTransport {
304
335
  argv,
305
336
  stdinBase64,
306
337
  ...(collaborationAuth === undefined ? {} : { collaborationAuth }),
307
- }, watchdogTimeout);
338
+ }, watchdogTimeout, {
339
+ requestTimeoutMs: supportsRequestTimeoutMetadata ? options.timeoutMs : undefined,
340
+ });
308
341
  if (typeof response !== 'object' || response == null || Array.isArray(response)) {
309
342
  throw new errors.SuperDocCliError('Host returned invalid cli.invoke result.', {
310
343
  code: 'HOST_PROTOCOL_ERROR',
@@ -64,6 +64,7 @@ export declare class HostTransport {
64
64
  dispose(): Promise<void>;
65
65
  private disposeAfterActiveCalls;
66
66
  invoke<TData = unknown>(operation: OperationSpec, params?: Record<string, unknown>, options?: TransportInvokeOptions): Promise<TData>;
67
+ replaceFile(sessionId: string, path: string, options?: TransportInvokeOptions): Promise<Record<string, unknown>>;
67
68
  private invokeWhileActive;
68
69
  private runWhileActive;
69
70
  private ensureConnected;
@@ -2,7 +2,7 @@ import { spawn } from 'node:child_process';
2
2
  import { createInterface } from 'node:readline';
3
3
  import { buildOperationArgv, normalizeCollaborationAuth, resolveInvocation, } from './transport-common.js';
4
4
  import { SuperDocCliError } from './errors.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';
5
+ import { DOCUMENT_RPC_FEATURES, DOCUMENT_RPC_DEFAULT_CHANGE_MODE_FEATURE, DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE, DOCUMENT_RPC_REPLACE_FILE_FEATURE, DOCUMENT_RPC_SOURCE_SAVE_FEATURE, buildDocumentInvokeParams, buildDocumentOpenParams, canOpenWithDocumentRpc, documentRpcRequestSupportsResponseTimeout, documentRpcEnabled, mapDocumentLifecycleResult, mapDocumentOpenResult, mapDocumentReplaceFileResult, 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
  export const COLLABORATION_AUTH_PER_OPEN_FEATURE = 'collaboration.auth.perOpen';
@@ -184,6 +184,34 @@ export class HostTransport {
184
184
  async invoke(operation, params = {}, options = {}) {
185
185
  return this.runWhileActive(() => this.invokeWhileActive(operation, params, options));
186
186
  }
187
+ async replaceFile(sessionId, path, options = {}) {
188
+ return this.runWhileActive(async () => {
189
+ await this.ensureConnected();
190
+ if (!this.hostFeatures.has(DOCUMENT_RPC_REPLACE_FILE_FEATURE)) {
191
+ throw new SuperDocCliError('The connected host does not support document replacement.', {
192
+ code: 'CAPABILITY_UNSUPPORTED',
193
+ details: { feature: DOCUMENT_RPC_REPLACE_FILE_FEATURE },
194
+ });
195
+ }
196
+ const tracksDirectSession = this.documentRpcSessions.has(sessionId);
197
+ if (tracksDirectSession)
198
+ this.beginDocumentRpcMutation(sessionId);
199
+ try {
200
+ const response = await this.sendJsonRpcRequest(DOCUMENT_RPC_REPLACE_FILE_FEATURE, { sessionId, path }, this.resolveWatchdogTimeout(options.timeoutMs));
201
+ const result = mapDocumentReplaceFileResult(response, sessionId);
202
+ if (tracksDirectSession)
203
+ this.settleDocumentRpcMutation(sessionId, 'applied');
204
+ return result;
205
+ }
206
+ catch (error) {
207
+ if (tracksDirectSession) {
208
+ const indeterminate = error instanceof SuperDocCliError && ['TIMEOUT', 'HOST_TIMEOUT', 'HOST_DISCONNECTED'].includes(error.code);
209
+ this.settleDocumentRpcMutation(sessionId, indeterminate ? 'indeterminate' : 'not-applied');
210
+ }
211
+ throw error;
212
+ }
213
+ });
214
+ }
187
215
  async invokeWhileActive(operation, params, options) {
188
216
  const collaborationAuth = options.collaborationAuth === undefined ? undefined : normalizeCollaborationAuth(options.collaborationAuth);
189
217
  if (collaborationAuth !== undefined) {
@@ -282,8 +310,11 @@ export class HostTransport {
282
310
  details: { operationId: operation.operationId },
283
311
  });
284
312
  }
313
+ const supportsRequestTimeoutMetadata = this.hostFeatures.has(DOCUMENT_RPC_REQUEST_TIMEOUT_FEATURE);
285
314
  const cliHostRequestTimeoutMs = this.requestTimeoutMs ?? HOST_DEFAULT_REQUEST_TIMEOUT_MS;
286
- if (options.timeoutMs !== undefined && options.timeoutMs > cliHostRequestTimeoutMs) {
315
+ if (!supportsRequestTimeoutMetadata &&
316
+ options.timeoutMs !== undefined &&
317
+ options.timeoutMs > cliHostRequestTimeoutMs) {
287
318
  throw new SuperDocCliError(`CLI host cannot honor timeoutMs=${options.timeoutMs} above its ${cliHostRequestTimeoutMs}ms request ceiling.`, {
288
319
  code: 'DOCUMENT_RPC_INPUT_UNSUPPORTED',
289
320
  details: {
@@ -301,7 +332,9 @@ export class HostTransport {
301
332
  argv,
302
333
  stdinBase64,
303
334
  ...(collaborationAuth === undefined ? {} : { collaborationAuth }),
304
- }, watchdogTimeout);
335
+ }, watchdogTimeout, {
336
+ requestTimeoutMs: supportsRequestTimeoutMetadata ? options.timeoutMs : undefined,
337
+ });
305
338
  if (typeof response !== 'object' || response == null || Array.isArray(response)) {
306
339
  throw new SuperDocCliError('Host returned invalid cli.invoke result.', {
307
340
  code: 'HOST_PROTOCOL_ERROR',
@@ -1,5 +1,8 @@
1
1
  'use strict';
2
2
 
3
+ var promises = require('node:fs/promises');
4
+ var os = require('node:os');
5
+ var path = require('node:path');
3
6
  var host = require('./host.cjs');
4
7
  var embeddedCli = require('./embedded-cli.cjs');
5
8
  var embeddedDocumentHost = require('./embedded-document-host.cjs');
@@ -88,6 +91,35 @@ class SuperDocRuntime {
88
91
  throw error;
89
92
  }
90
93
  }
94
+ async replaceFile(sessionId, source, options = {}) {
95
+ if (typeof source === 'string') {
96
+ if (source.trim().length === 0) {
97
+ throw new errors.SuperDocCliError('replaceFile source path must be non-empty.', { code: 'INVALID_ARGUMENT' });
98
+ }
99
+ return (await this.transport.replaceFile(sessionId, source, options));
100
+ }
101
+ let bytes;
102
+ if (source instanceof Uint8Array) {
103
+ bytes = new Uint8Array(source);
104
+ }
105
+ else if (source instanceof ArrayBuffer) {
106
+ bytes = new Uint8Array(source.slice(0));
107
+ }
108
+ else {
109
+ throw new errors.SuperDocCliError('replaceFile source must be a path, Uint8Array, or ArrayBuffer.', {
110
+ code: 'INVALID_ARGUMENT',
111
+ });
112
+ }
113
+ const stagingDirectory = await promises.mkdtemp(path.join(os.tmpdir(), 'superdoc-sdk-replace-'));
114
+ const stagedPath = path.join(stagingDirectory, 'replacement.docx');
115
+ try {
116
+ await promises.writeFile(stagedPath, bytes, { mode: 0o600 });
117
+ return (await this.transport.replaceFile(sessionId, stagedPath, options));
118
+ }
119
+ finally {
120
+ await promises.rm(stagingDirectory, { recursive: true, force: true });
121
+ }
122
+ }
91
123
  }
92
124
 
93
125
  exports.SuperDocRuntime = SuperDocRuntime;
@@ -7,6 +7,19 @@ type EmbeddedRuntimeResolvers = {
7
7
  cli: () => string;
8
8
  documentHost: () => string;
9
9
  };
10
+ export type ReplaceFileSource = string | Uint8Array | ArrayBuffer;
11
+ export interface ReplaceFileOptions {
12
+ readonly timeoutMs?: number;
13
+ }
14
+ export interface ReplaceFileResult {
15
+ readonly contextId: string;
16
+ readonly runtime: 'v2';
17
+ readonly replaced: true;
18
+ readonly document: {
19
+ readonly byteLength: number;
20
+ readonly revision: string;
21
+ };
22
+ }
10
23
  export declare function resolveRuntimeProcess(options?: SuperDocClientOptions, embedded?: EmbeddedRuntimeResolvers): RuntimeProcessResolution;
11
24
  export declare function toSafeInvokeTraceOptions(options: TransportInvokeOptions): InvokeOptions;
12
25
  /**
@@ -21,5 +34,6 @@ export declare class SuperDocRuntime {
21
34
  connect(): Promise<void>;
22
35
  dispose(): Promise<void>;
23
36
  invoke<TData = unknown>(operation: OperationSpec, params?: Record<string, unknown>, options?: TransportInvokeOptions): Promise<TData>;
37
+ replaceFile(sessionId: string, source: ReplaceFileSource, options?: ReplaceFileOptions): Promise<ReplaceFileResult>;
24
38
  }
25
39
  export type { CollaborationAuth, DocumentRuntimeKind, DocOpenOptions, InvokeOptions, OperationParamSpec, OperationSpec, RuntimeInvoker, SuperDocClientOptions, };
@@ -1,3 +1,6 @@
1
+ import { mkdtemp, rm, writeFile } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
1
4
  import { HostTransport } from './host.js';
2
5
  import { resolveEmbeddedCliBinary } from './embedded-cli.js';
3
6
  import { resolveEmbeddedDocumentHostBinary } from './embedded-document-host.js';
@@ -85,4 +88,33 @@ export class SuperDocRuntime {
85
88
  throw error;
86
89
  }
87
90
  }
91
+ async replaceFile(sessionId, source, options = {}) {
92
+ if (typeof source === 'string') {
93
+ if (source.trim().length === 0) {
94
+ throw new SuperDocCliError('replaceFile source path must be non-empty.', { code: 'INVALID_ARGUMENT' });
95
+ }
96
+ return (await this.transport.replaceFile(sessionId, source, options));
97
+ }
98
+ let bytes;
99
+ if (source instanceof Uint8Array) {
100
+ bytes = new Uint8Array(source);
101
+ }
102
+ else if (source instanceof ArrayBuffer) {
103
+ bytes = new Uint8Array(source.slice(0));
104
+ }
105
+ else {
106
+ throw new SuperDocCliError('replaceFile source must be a path, Uint8Array, or ArrayBuffer.', {
107
+ code: 'INVALID_ARGUMENT',
108
+ });
109
+ }
110
+ const stagingDirectory = await mkdtemp(join(tmpdir(), 'superdoc-sdk-replace-'));
111
+ const stagedPath = join(stagingDirectory, 'replacement.docx');
112
+ try {
113
+ await writeFile(stagedPath, bytes, { mode: 0o600 });
114
+ return (await this.transport.replaceFile(sessionId, stagedPath, options));
115
+ }
116
+ finally {
117
+ await rm(stagingDirectory, { recursive: true, force: true });
118
+ }
119
+ }
88
120
  }
@@ -3,6 +3,6 @@
3
3
  // AUTO-GENERATED by scripts/embed-version.mjs — DO NOT EDIT.
4
4
  // Source of truth: package.json. Regenerated on every SDK build so the
5
5
  // SDK retains its own version identity when bundled into another package.
6
- const SDK_VERSION = '2.10.0-next.4';
6
+ const SDK_VERSION = '2.10.0-next.5';
7
7
 
8
8
  exports.SDK_VERSION = SDK_VERSION;
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "2.10.0-next.4";
1
+ export declare const SDK_VERSION = "2.10.0-next.5";
@@ -1,4 +1,4 @@
1
1
  // AUTO-GENERATED by scripts/embed-version.mjs — DO NOT EDIT.
2
2
  // Source of truth: package.json. Regenerated on every SDK build so the
3
3
  // SDK retains its own version identity when bundled into another package.
4
- export const SDK_VERSION = '2.10.0-next.4';
4
+ export const SDK_VERSION = '2.10.0-next.5';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superdoc/sdk",
3
- "version": "2.10.0-next.4",
3
+ "version": "2.10.0-next.5",
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,11 +37,11 @@
37
37
  "typescript": "^5.9.2"
38
38
  },
39
39
  "optionalDependencies": {
40
- "@superdoc/sdk-darwin-arm64": "2.10.0-next.4",
41
- "@superdoc/sdk-darwin-x64": "2.10.0-next.4",
42
- "@superdoc/sdk-linux-x64": "2.10.0-next.4",
43
- "@superdoc/sdk-linux-arm64": "2.10.0-next.4",
44
- "@superdoc/sdk-windows-x64": "2.10.0-next.4"
40
+ "@superdoc/sdk-darwin-arm64": "2.10.0-next.5",
41
+ "@superdoc/sdk-darwin-x64": "2.10.0-next.5",
42
+ "@superdoc/sdk-linux-x64": "2.10.0-next.5",
43
+ "@superdoc/sdk-linux-arm64": "2.10.0-next.5",
44
+ "@superdoc/sdk-windows-x64": "2.10.0-next.5"
45
45
  },
46
46
  "publishConfig": {
47
47
  "access": "public"
@@ -50,7 +50,7 @@
50
50
  "build": "rm -rf dist && node scripts/embed-version.mjs && node scripts/embed-prompts.mjs && node scripts/embed-tools.mjs && tsc && pnpm run typecheck:consumer && 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
- "typecheck:consumer": "tsc --noEmit --strict --skipLibCheck --target ES2022 --module NodeNext --moduleResolution NodeNext ../../../../tests/consumer-typecheck/src/sdk-per-open-collaboration-auth.mts",
53
+ "typecheck:consumer": "tsc --noEmit --strict --skipLibCheck --target ES2022 --module NodeNext --moduleResolution NodeNext ../../../../tests/consumer-typecheck/src/sdk-per-open-collaboration-auth.mts ../../../../tests/consumer-typecheck/src/sdk-replace-file.mts",
54
54
  "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",
55
55
  "smoke:product-action": "node scripts/product-action-smoke.mjs"
56
56
  }