@superdoc-dev/cli 0.2.0-next.52 → 0.2.0-next.54

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 (2) hide show
  1. package/dist/index.js +263 -27
  2. package/package.json +8 -8
package/dist/index.js CHANGED
@@ -8292,6 +8292,16 @@ function normalizeSessionType(value) {
8292
8292
  return "collab";
8293
8293
  return "local";
8294
8294
  }
8295
+ function normalizeUser(value) {
8296
+ const record = asRecord(value);
8297
+ if (!record)
8298
+ return;
8299
+ if (typeof record.name !== "string" || record.name.length === 0)
8300
+ return;
8301
+ if (typeof record.email !== "string")
8302
+ return;
8303
+ return { name: record.name, email: record.email };
8304
+ }
8295
8305
  function normalizeCollaborationProfile(value) {
8296
8306
  const record = asRecord(value);
8297
8307
  if (!record)
@@ -8301,6 +8311,8 @@ function normalizeCollaborationProfile(value) {
8301
8311
  const documentId = record.documentId;
8302
8312
  const tokenEnv = record.tokenEnv;
8303
8313
  const syncTimeoutMs = record.syncTimeoutMs;
8314
+ const onMissing = record.onMissing;
8315
+ const bootstrapSettlingMs = record.bootstrapSettlingMs;
8304
8316
  if (providerType !== "hocuspocus" && providerType !== "y-websocket")
8305
8317
  return;
8306
8318
  if (typeof url !== "string" || url.length === 0)
@@ -8312,28 +8324,39 @@ function normalizeCollaborationProfile(value) {
8312
8324
  if (syncTimeoutMs != null && (typeof syncTimeoutMs !== "number" || !Number.isFinite(syncTimeoutMs) || syncTimeoutMs <= 0)) {
8313
8325
  return;
8314
8326
  }
8327
+ if (onMissing != null && onMissing !== "seedFromDoc" && onMissing !== "blank" && onMissing !== "error") {
8328
+ return;
8329
+ }
8330
+ if (bootstrapSettlingMs != null && (typeof bootstrapSettlingMs !== "number" || !Number.isFinite(bootstrapSettlingMs) || bootstrapSettlingMs <= 0)) {
8331
+ return;
8332
+ }
8315
8333
  return {
8316
8334
  providerType,
8317
8335
  url,
8318
8336
  documentId,
8319
8337
  tokenEnv: typeof tokenEnv === "string" ? tokenEnv : undefined,
8320
- syncTimeoutMs: typeof syncTimeoutMs === "number" ? syncTimeoutMs : undefined
8338
+ syncTimeoutMs: typeof syncTimeoutMs === "number" ? syncTimeoutMs : undefined,
8339
+ onMissing,
8340
+ bootstrapSettlingMs: typeof bootstrapSettlingMs === "number" ? bootstrapSettlingMs : undefined
8321
8341
  };
8322
8342
  }
8323
8343
  function normalizeContextMetadata(metadata) {
8324
8344
  const sessionType = normalizeSessionType(metadata.sessionType);
8325
8345
  const collaboration = normalizeCollaborationProfile(metadata.collaboration);
8346
+ const user = normalizeUser(metadata.user);
8326
8347
  if (sessionType === "collab" && collaboration) {
8327
8348
  return {
8328
8349
  ...metadata,
8329
8350
  sessionType,
8330
- collaboration
8351
+ collaboration,
8352
+ user
8331
8353
  };
8332
8354
  }
8333
8355
  return {
8334
8356
  ...metadata,
8335
8357
  sessionType: "local",
8336
- collaboration: undefined
8358
+ collaboration: undefined,
8359
+ user
8337
8360
  };
8338
8361
  }
8339
8362
  function sleep(ms) {
@@ -8747,6 +8770,7 @@ function createInitialContextMetadata(io, paths, contextId, input) {
8747
8770
  revision: 0,
8748
8771
  sessionType,
8749
8772
  collaboration: sessionType === "collab" ? input.collaboration : undefined,
8773
+ user: input.user,
8750
8774
  openedAt: timestamp,
8751
8775
  updatedAt: timestamp,
8752
8776
  sourceSnapshot: input.sourceSnapshot
@@ -9196,6 +9220,14 @@ var init_cli_only_operation_definitions = __esm(() => {
9196
9220
  documentId: { type: "string" },
9197
9221
  url: { type: "string" }
9198
9222
  }
9223
+ },
9224
+ bootstrap: {
9225
+ type: "object",
9226
+ properties: {
9227
+ roomState: { type: "string" },
9228
+ bootstrapApplied: { type: "boolean" },
9229
+ bootstrapSource: { type: "string" }
9230
+ }
9199
9231
  }
9200
9232
  },
9201
9233
  required: ["contextId", "sessionType"]
@@ -83665,18 +83697,35 @@ function parseCollaborationInput(value) {
83665
83697
  if ("params" in value) {
83666
83698
  throw new CliError("VALIDATION_ERROR", "collaboration.params is not supported in v1.");
83667
83699
  }
83668
- const allowedKeys = new Set(["providerType", "url", "documentId", "tokenEnv", "syncTimeoutMs"]);
83700
+ const allowedKeys = new Set([
83701
+ "providerType",
83702
+ "url",
83703
+ "documentId",
83704
+ "tokenEnv",
83705
+ "syncTimeoutMs",
83706
+ "onMissing",
83707
+ "bootstrapSettlingMs"
83708
+ ]);
83669
83709
  for (const key of Object.keys(value)) {
83670
83710
  if (!allowedKeys.has(key)) {
83671
83711
  throw new CliError("VALIDATION_ERROR", `collaboration.${key} is not supported.`);
83672
83712
  }
83673
83713
  }
83714
+ let onMissing;
83715
+ if (value.onMissing != null) {
83716
+ if (value.onMissing !== "seedFromDoc" && value.onMissing !== "blank" && value.onMissing !== "error") {
83717
+ throw new CliError("VALIDATION_ERROR", 'collaboration.onMissing must be "seedFromDoc", "blank", or "error".');
83718
+ }
83719
+ onMissing = value.onMissing;
83720
+ }
83674
83721
  return {
83675
83722
  providerType: normalizeProviderType(value.providerType, "collaboration.providerType"),
83676
83723
  url: expectNonEmptyString(value.url, "collaboration.url").trim(),
83677
83724
  documentId: value.documentId != null ? expectNonEmptyString(value.documentId, "collaboration.documentId") : undefined,
83678
83725
  tokenEnv: expectOptionalEnvVarName(value.tokenEnv, "collaboration.tokenEnv"),
83679
- syncTimeoutMs: expectOptionalPositiveNumber(value.syncTimeoutMs, "collaboration.syncTimeoutMs")
83726
+ syncTimeoutMs: expectOptionalPositiveNumber(value.syncTimeoutMs, "collaboration.syncTimeoutMs"),
83727
+ onMissing,
83728
+ bootstrapSettlingMs: expectOptionalPositiveNumber(value.bootstrapSettlingMs, "collaboration.bootstrapSettlingMs")
83680
83729
  };
83681
83730
  }
83682
83731
  function resolveCollaborationProfile(input, sessionId) {
@@ -83686,7 +83735,9 @@ function resolveCollaborationProfile(input, sessionId) {
83686
83735
  url: input.url,
83687
83736
  documentId,
83688
83737
  tokenEnv: input.tokenEnv,
83689
- syncTimeoutMs: input.syncTimeoutMs
83738
+ syncTimeoutMs: input.syncTimeoutMs,
83739
+ onMissing: input.onMissing,
83740
+ bootstrapSettlingMs: input.bootstrapSettlingMs
83690
83741
  };
83691
83742
  }
83692
83743
  function resolveCollaborationToken(profile) {
@@ -83786,6 +83837,119 @@ var init_collaboration = __esm(() => {
83786
83837
  ENV_VAR_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
83787
83838
  });
83788
83839
 
83840
+ // src/lib/bootstrap.ts
83841
+ function detectRoomState(ydoc) {
83842
+ const fragment = ydoc.getXmlFragment("supereditor");
83843
+ if (fragment.length > 0)
83844
+ return "populated";
83845
+ const metaMap = ydoc.getMap("meta");
83846
+ for (const [key, value] of metaMap.entries()) {
83847
+ if (key === "bootstrap") {
83848
+ const marker = value;
83849
+ if (marker && marker.source !== "pending")
83850
+ return "populated";
83851
+ continue;
83852
+ }
83853
+ return "populated";
83854
+ }
83855
+ return "empty";
83856
+ }
83857
+ function resolveBootstrapDecision(roomState, onMissing, hasDoc) {
83858
+ if (roomState === "populated")
83859
+ return { action: "join" };
83860
+ switch (onMissing) {
83861
+ case "seedFromDoc":
83862
+ return { action: "seed", source: hasDoc ? "doc" : "blank" };
83863
+ case "blank":
83864
+ return { action: "seed", source: "blank" };
83865
+ case "error":
83866
+ return { action: "error", reason: 'Collaboration room is empty and onMissing is set to "error".' };
83867
+ }
83868
+ }
83869
+ function writeBootstrapMarker(ydoc, source) {
83870
+ const metaMap = ydoc.getMap("meta");
83871
+ const marker = {
83872
+ version: 1,
83873
+ clientId: ydoc.clientID,
83874
+ seededAt: new Date().toISOString(),
83875
+ source
83876
+ };
83877
+ metaMap.set("bootstrap", marker);
83878
+ }
83879
+ function readBootstrapMarker(ydoc) {
83880
+ return ydoc.getMap("meta").get("bootstrap");
83881
+ }
83882
+ function snapshotCompetitor(marker) {
83883
+ return {
83884
+ observedOtherClientId: marker.clientId,
83885
+ observedSource: marker.source,
83886
+ observedAt: new Date().toISOString()
83887
+ };
83888
+ }
83889
+ function observeCompetitor(ydoc) {
83890
+ const myClientId = ydoc.clientID;
83891
+ const metaMap = ydoc.getMap("meta");
83892
+ let competitor = null;
83893
+ const handler2 = (event) => {
83894
+ if (!event.keysChanged.has("bootstrap"))
83895
+ return;
83896
+ const marker = metaMap.get("bootstrap");
83897
+ if (marker && marker.clientId !== myClientId && !competitor) {
83898
+ competitor = snapshotCompetitor(marker);
83899
+ }
83900
+ };
83901
+ metaMap.observe(handler2);
83902
+ return {
83903
+ getCompetitor: () => competitor,
83904
+ dispose: () => metaMap.unobserve(handler2)
83905
+ };
83906
+ }
83907
+ function sleep2(ms) {
83908
+ if (ms <= 0)
83909
+ return Promise.resolve();
83910
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
83911
+ }
83912
+ async function claimBootstrap(ydoc, settlingMs, jitterMs = DEFAULT_BOOTSTRAP_JITTER_MS) {
83913
+ await sleep2(Math.floor(Math.random() * jitterMs));
83914
+ const metaMap = ydoc.getMap("meta");
83915
+ metaMap.set("bootstrap", {
83916
+ version: 1,
83917
+ clientId: ydoc.clientID,
83918
+ seededAt: new Date().toISOString(),
83919
+ source: "pending"
83920
+ });
83921
+ const observer = observeCompetitor(ydoc);
83922
+ try {
83923
+ await sleep2(settlingMs);
83924
+ const competitor = observer.getCompetitor();
83925
+ if (competitor)
83926
+ return { granted: false, competitor };
83927
+ const marker = readBootstrapMarker(ydoc);
83928
+ if (marker?.clientId === ydoc.clientID) {
83929
+ return { granted: true };
83930
+ }
83931
+ return {
83932
+ granted: false,
83933
+ competitor: marker ? snapshotCompetitor(marker) : { observedOtherClientId: 0, observedSource: "unknown", observedAt: new Date().toISOString() }
83934
+ };
83935
+ } finally {
83936
+ observer.dispose();
83937
+ }
83938
+ }
83939
+ async function detectBootstrapRace(ydoc, observeMs = POST_SEED_OBSERVE_MS) {
83940
+ const observer = observeCompetitor(ydoc);
83941
+ try {
83942
+ await sleep2(observeMs);
83943
+ const competitor = observer.getCompetitor();
83944
+ if (competitor)
83945
+ return { raceSuspected: true, competitor };
83946
+ return { raceSuspected: false };
83947
+ } finally {
83948
+ observer.dispose();
83949
+ }
83950
+ }
83951
+ var DEFAULT_BOOTSTRAP_SETTLING_MS = 1500, DEFAULT_BOOTSTRAP_JITTER_MS = 150, POST_SEED_OBSERVE_MS = 200;
83952
+
83789
83953
  // src/lib/document.ts
83790
83954
  import { readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
83791
83955
  import { createHash as createHash2 } from "node:crypto";
@@ -83879,10 +84043,11 @@ async function openDocument(doc, io, options = {}) {
83879
84043
  const isTest = false;
83880
84044
  editor = await EditorRuntime.open(Buffer.from(source), {
83881
84045
  documentId: options.documentId ?? meta.path ?? "blank.docx",
83882
- user: { id: "cli", name: "CLI" },
84046
+ user: options.user ? { name: options.user.name, email: options.user.email, image: null } : { id: "cli", name: "CLI" },
83883
84047
  ...isTest ? { telemetry: { enabled: false } } : {},
83884
84048
  ydoc: options.ydoc,
83885
84049
  ...options.collaborationProvider != null ? { collaborationProvider: options.collaborationProvider } : {},
84050
+ ...options.isNewFile != null ? { isNewFile: options.isNewFile } : {},
83886
84051
  ...passThroughEditorOpts
83887
84052
  });
83888
84053
  } catch (error) {
@@ -83939,18 +84104,48 @@ async function openDocument(doc, io, options = {}) {
83939
84104
  }
83940
84105
  };
83941
84106
  }
83942
- async function openCollaborativeDocument(doc, io, profile) {
84107
+ async function openCollaborativeDocument(doc, io, profile, options = {}) {
83943
84108
  const runtime = createCollaborationRuntime(profile);
83944
84109
  try {
83945
84110
  await runtime.waitForSync();
83946
- const opened = await openDocument(doc, io, {
84111
+ const onMissing = profile.onMissing ?? "seedFromDoc";
84112
+ let finalRoomState = detectRoomState(runtime.ydoc);
84113
+ let decision = resolveBootstrapDecision(finalRoomState, onMissing, doc != null);
84114
+ if (decision.action === "seed") {
84115
+ const claim = await claimBootstrap(runtime.ydoc, profile.bootstrapSettlingMs ?? DEFAULT_BOOTSTRAP_SETTLING_MS);
84116
+ if (!claim.granted) {
84117
+ finalRoomState = detectRoomState(runtime.ydoc);
84118
+ decision = { action: "join" };
84119
+ }
84120
+ }
84121
+ if (decision.action === "error") {
84122
+ throw new CliError("COLLABORATION_ROOM_EMPTY", decision.reason);
84123
+ }
84124
+ const shouldSeed = decision.action === "seed";
84125
+ const docForEditor = shouldSeed ? doc : undefined;
84126
+ const opened = await openDocument(docForEditor, io, {
83947
84127
  documentId: profile.documentId,
83948
84128
  ydoc: runtime.ydoc,
83949
- collaborationProvider: runtime.provider
84129
+ collaborationProvider: runtime.provider,
84130
+ isNewFile: shouldSeed,
84131
+ user: options.user
83950
84132
  });
84133
+ let raceDetection;
84134
+ if (shouldSeed) {
84135
+ writeBootstrapMarker(runtime.ydoc, decision.source);
84136
+ raceDetection = await detectBootstrapRace(runtime.ydoc);
84137
+ }
84138
+ const bootstrap = {
84139
+ roomState: finalRoomState,
84140
+ bootstrapApplied: shouldSeed,
84141
+ bootstrapSource: shouldSeed ? decision.source : undefined,
84142
+ raceSuspected: raceDetection?.raceSuspected,
84143
+ raceCompetitor: raceDetection?.raceSuspected ? raceDetection.competitor : undefined
84144
+ };
83951
84145
  return {
83952
84146
  editor: opened.editor,
83953
84147
  meta: opened.meta,
84148
+ bootstrap,
83954
84149
  dispose() {
83955
84150
  try {
83956
84151
  opened.dispose();
@@ -83966,7 +84161,7 @@ async function openCollaborativeDocument(doc, io, profile) {
83966
84161
  }
83967
84162
  async function openSessionDocument(doc, io, metadata, options = {}) {
83968
84163
  if (metadata.sessionType !== "collab") {
83969
- return openDocument(doc, io);
84164
+ return openDocument(doc, io, { user: metadata.user });
83970
84165
  }
83971
84166
  if (!metadata.collaboration) {
83972
84167
  throw new CliError("COMMAND_FAILED", "Session is marked as collaborative but has no collaboration profile.");
@@ -83981,11 +84176,12 @@ async function openSessionDocument(doc, io, metadata, options = {}) {
83981
84176
  sessionType: metadata.sessionType,
83982
84177
  collaboration: metadata.collaboration,
83983
84178
  sourcePath: metadata.sourcePath,
83984
- workingDocPath: metadata.workingDocPath
84179
+ workingDocPath: metadata.workingDocPath,
84180
+ user: metadata.user
83985
84181
  };
83986
84182
  return options.collabSessionPool.acquire(sessionId, doc, metadataForPool, io);
83987
84183
  }
83988
- return openCollaborativeDocument(doc, io, metadata.collaboration);
84184
+ return openCollaborativeDocument(doc, io, metadata.collaboration, { user: metadata.user });
83989
84185
  }
83990
84186
  async function getFileChecksum(path2) {
83991
84187
  let bytes;
@@ -85445,7 +85641,7 @@ async function executeReadOperation(request) {
85445
85641
  opened2.dispose();
85446
85642
  }
85447
85643
  }
85448
- const opened = await openDocument(paths.workingDocPath, context.io);
85644
+ const opened = await openDocument(paths.workingDocPath, context.io, { user: metadata.user });
85449
85645
  try {
85450
85646
  const result = invokeOperation(opened.editor, operationId, input);
85451
85647
  const document4 = {
@@ -85635,7 +85831,7 @@ async function executeMutationOperation(request) {
85635
85831
  opened2.dispose();
85636
85832
  }
85637
85833
  }
85638
- const opened = await openDocument(paths.workingDocPath, context.io);
85834
+ const opened = await openDocument(paths.workingDocPath, context.io, { user: metadata.user });
85639
85835
  try {
85640
85836
  const result = invokeOperation2(opened.editor, operationId, input, invokeOptions);
85641
85837
  const document4 = {
@@ -86344,7 +86540,7 @@ function deriveOptionSpecs(params3) {
86344
86540
  }
86345
86541
  return specs;
86346
86542
  }
86347
- var DOC_PARAM, SESSION_PARAM, OUT_PARAM, FORCE_PARAM, DRY_RUN_PARAM, CHANGE_MODE_PARAM, EXPECTED_REVISION_PARAM, AGENT_HIDDEN_PARAM_NAMES, OPERATION_CONSTRAINTS, PARAM_FLAG_OVERRIDES, PARAM_SCHEMA_OVERRIDES, PARAM_EXCLUSIONS, TEXT_TARGET_FLAT_PARAMS, INSERT_FLAT_PARAMS, LIST_TARGET_FLAT_PARAMS, FORMAT_OPERATION_IDS, EXTRA_CLI_PARAMS, CLI_ONLY_METADATA, CLI_OPERATION_METADATA, CLI_OPERATION_OPTION_SPECS;
86543
+ var DOC_PARAM, SESSION_PARAM, OUT_PARAM, FORCE_PARAM, DRY_RUN_PARAM, CHANGE_MODE_PARAM, EXPECTED_REVISION_PARAM, USER_NAME_PARAM, USER_EMAIL_PARAM, AGENT_HIDDEN_PARAM_NAMES, OPERATION_CONSTRAINTS, PARAM_FLAG_OVERRIDES, PARAM_SCHEMA_OVERRIDES, PARAM_EXCLUSIONS, TEXT_TARGET_FLAT_PARAMS, INSERT_FLAT_PARAMS, LIST_TARGET_FLAT_PARAMS, FORMAT_OPERATION_IDS, EXTRA_CLI_PARAMS, CLI_ONLY_METADATA, CLI_OPERATION_METADATA, CLI_OPERATION_OPTION_SPECS;
86348
86544
  var init_operation_params = __esm(() => {
86349
86545
  init_src();
86350
86546
  init_operation_set();
@@ -86375,6 +86571,18 @@ var init_operation_params = __esm(() => {
86375
86571
  type: "number",
86376
86572
  agentVisible: false
86377
86573
  };
86574
+ USER_NAME_PARAM = {
86575
+ name: "userName",
86576
+ kind: "flag",
86577
+ flag: "user-name",
86578
+ type: "string"
86579
+ };
86580
+ USER_EMAIL_PARAM = {
86581
+ name: "userEmail",
86582
+ kind: "flag",
86583
+ flag: "user-email",
86584
+ type: "string"
86585
+ };
86378
86586
  AGENT_HIDDEN_PARAM_NAMES = new Set(["out", "expectedRevision", "changeMode", "dryRun"]);
86379
86587
  OPERATION_CONSTRAINTS = {
86380
86588
  "doc.find": {
@@ -86501,7 +86709,11 @@ var init_operation_params = __esm(() => {
86501
86709
  { name: "collabDocumentId", kind: "flag", flag: "collab-document-id", type: "string" },
86502
86710
  { name: "collabUrl", kind: "flag", flag: "collab-url", type: "string" },
86503
86711
  { name: "contentOverride", kind: "flag", flag: "content-override", type: "string" },
86504
- { name: "overrideType", kind: "flag", flag: "override-type", type: "string" }
86712
+ { name: "overrideType", kind: "flag", flag: "override-type", type: "string" },
86713
+ { name: "onMissing", kind: "flag", flag: "on-missing", type: "string" },
86714
+ { name: "bootstrapSettlingMs", kind: "flag", flag: "bootstrap-settling-ms", type: "number" },
86715
+ USER_NAME_PARAM,
86716
+ USER_EMAIL_PARAM
86505
86717
  ],
86506
86718
  constraints: null
86507
86719
  },
@@ -87800,6 +88012,10 @@ async function runOpen(tokens, context) {
87800
88012
  const collabDocumentId = getStringOption(parsed, "collab-document-id");
87801
88013
  const contentOverride = getStringOption(parsed, "content-override");
87802
88014
  const overrideType = getStringOption(parsed, "override-type");
88015
+ const onMissing = getStringOption(parsed, "on-missing");
88016
+ const bootstrapSettlingMs = getNumberOption(parsed, "bootstrap-settling-ms");
88017
+ const userName = getStringOption(parsed, "user-name");
88018
+ const userEmail = getStringOption(parsed, "user-email");
87803
88019
  if (contentOverride != null && !overrideType) {
87804
88020
  throw new CliError("INVALID_ARGUMENT", "open: --content-override requires --override-type.");
87805
88021
  }
@@ -87809,6 +88025,9 @@ async function runOpen(tokens, context) {
87809
88025
  if (overrideType && !VALID_OVERRIDE_TYPES.has(overrideType)) {
87810
88026
  throw new CliError("INVALID_ARGUMENT", `open: --override-type must be one of: markdown, html, text. Got "${overrideType}".`);
87811
88027
  }
88028
+ if (onMissing != null && !VALID_ON_MISSING.has(onMissing)) {
88029
+ throw new CliError("INVALID_ARGUMENT", `open: --on-missing must be one of: seedFromDoc, blank, error. Got "${onMissing}".`);
88030
+ }
87812
88031
  if (collaborationPayload != null && (collabUrl || collabDocumentId)) {
87813
88032
  throw new CliError("INVALID_ARGUMENT", "open: do not combine --collaboration-json with --collab-url / --collab-document-id.");
87814
88033
  }
@@ -87817,18 +88036,32 @@ async function runOpen(tokens, context) {
87817
88036
  }
87818
88037
  let collaborationInput;
87819
88038
  if (collaborationPayload != null) {
87820
- collaborationInput = parseCollaborationInput(collaborationPayload);
88039
+ if (typeof collaborationPayload !== "object" || Array.isArray(collaborationPayload)) {
88040
+ throw new CliError("VALIDATION_ERROR", "open: --collaboration-json must be a JSON object.");
88041
+ }
88042
+ const payload = collaborationPayload;
88043
+ if (onMissing != null && !("onMissing" in payload))
88044
+ payload.onMissing = onMissing;
88045
+ if (bootstrapSettlingMs != null && !("bootstrapSettlingMs" in payload))
88046
+ payload.bootstrapSettlingMs = bootstrapSettlingMs;
88047
+ collaborationInput = parseCollaborationInput(payload);
87821
88048
  } else if (collabUrl) {
87822
88049
  collaborationInput = parseCollaborationInput({
87823
88050
  providerType: "hocuspocus",
87824
88051
  url: collabUrl,
87825
- documentId: collabDocumentId
88052
+ documentId: collabDocumentId,
88053
+ ...onMissing != null ? { onMissing } : {},
88054
+ ...bootstrapSettlingMs != null ? { bootstrapSettlingMs } : {}
87826
88055
  });
87827
88056
  } else if (collabDocumentId) {
87828
88057
  throw new CliError("MISSING_REQUIRED", "open: --collab-document-id requires --collab-url.");
87829
88058
  }
87830
88059
  const collaboration = collaborationInput ? resolveCollaborationProfile(collaborationInput, sessionId) : undefined;
87831
88060
  const sessionType = collaboration ? "collab" : "local";
88061
+ if (!collaboration && (onMissing != null || bootstrapSettlingMs != null)) {
88062
+ throw new CliError("INVALID_ARGUMENT", "open: --on-missing and --bootstrap-settling-ms require collaboration mode (--collaboration-json or --collab-url).");
88063
+ }
88064
+ const user = userName != null || userEmail != null ? { name: userName ?? "CLI", email: userEmail ?? "" } : undefined;
87832
88065
  const editorOpenOptions = {};
87833
88066
  if (contentOverride != null && overrideType) {
87834
88067
  if (overrideType === "markdown") {
@@ -87854,10 +88087,8 @@ async function runOpen(tokens, context) {
87854
88087
  revision: existing.revision
87855
88088
  });
87856
88089
  }
87857
- if (collaboration && doc == null) {
87858
- throw new CliError("MISSING_REQUIRED", "open: a document path is required when using collaboration.");
87859
- }
87860
- const opened = collaboration ? await openCollaborativeDocument(doc, context.io, collaboration) : await openDocument(doc, context.io, { editorOpenOptions });
88090
+ const opened = collaboration ? await openCollaborativeDocument(doc, context.io, collaboration, { user }) : await openDocument(doc, context.io, { editorOpenOptions, user });
88091
+ const bootstrap = "bootstrap" in opened ? opened.bootstrap : undefined;
87861
88092
  let adoptedToHostPool = false;
87862
88093
  try {
87863
88094
  const output = await exportToPath(opened.editor, paths.workingDocPath, true);
@@ -87868,7 +88099,8 @@ async function runOpen(tokens, context) {
87868
88099
  sourcePath,
87869
88100
  sourceSnapshot,
87870
88101
  sessionType,
87871
- collaboration
88102
+ collaboration,
88103
+ user
87872
88104
  });
87873
88105
  await writeContextMetadata(paths, metadata);
87874
88106
  await setActiveSessionId(metadata.contextId);
@@ -87890,6 +88122,7 @@ async function runOpen(tokens, context) {
87890
88122
  dirty: metadata.dirty,
87891
88123
  sessionType: metadata.sessionType,
87892
88124
  collaboration: metadata.collaboration,
88125
+ bootstrap,
87893
88126
  openedAt: metadata.openedAt,
87894
88127
  updatedAt: metadata.updatedAt
87895
88128
  },
@@ -87902,7 +88135,7 @@ async function runOpen(tokens, context) {
87902
88135
  }
87903
88136
  }, undefined, sessionId);
87904
88137
  }
87905
- var VALID_OVERRIDE_TYPES;
88138
+ var VALID_OVERRIDE_TYPES, VALID_ON_MISSING;
87906
88139
  var init_open = __esm(() => {
87907
88140
  init_args();
87908
88141
  init_collaboration();
@@ -87912,6 +88145,7 @@ var init_open = __esm(() => {
87912
88145
  init_operation_args();
87913
88146
  init_session();
87914
88147
  VALID_OVERRIDE_TYPES = new Set(["markdown", "html", "text"]);
88148
+ VALID_ON_MISSING = new Set(["seedFromDoc", "blank", "error"]);
87915
88149
  });
87916
88150
 
87917
88151
  // src/commands/save.ts
@@ -94374,7 +94608,9 @@ function profileToKey(profile) {
94374
94608
  url: profile.url,
94375
94609
  documentId: profile.documentId,
94376
94610
  tokenEnv: profile.tokenEnv ?? null,
94377
- syncTimeoutMs: profile.syncTimeoutMs ?? null
94611
+ syncTimeoutMs: profile.syncTimeoutMs ?? null,
94612
+ onMissing: profile.onMissing ?? null,
94613
+ bootstrapSettlingMs: profile.bootstrapSettlingMs ?? null
94378
94614
  });
94379
94615
  }
94380
94616
  function buildFingerprint(metadata) {
@@ -94417,7 +94653,7 @@ class InMemoryCollaborationSessionPool {
94417
94653
  await this.disposeSession(sessionId);
94418
94654
  }
94419
94655
  const profile = metadata.collaboration;
94420
- const opened = await this.openCollaborative(docPath, io, profile);
94656
+ const opened = await this.openCollaborative(docPath, io, profile, { user: metadata.user });
94421
94657
  const created = {
94422
94658
  opened,
94423
94659
  fingerprint,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superdoc-dev/cli",
3
- "version": "0.2.0-next.52",
3
+ "version": "0.2.0-next.54",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "superdoc": "./dist/index.js"
@@ -20,20 +20,20 @@
20
20
  "@types/node": "22.19.2",
21
21
  "typescript": "^5.9.2",
22
22
  "@superdoc/document-api": "0.0.1",
23
- "superdoc": "1.16.0",
24
23
  "@superdoc/super-editor": "0.0.1",
25
- "@superdoc/pm-adapter": "0.0.0"
24
+ "@superdoc/pm-adapter": "0.0.0",
25
+ "superdoc": "1.16.0"
26
26
  },
27
27
  "module": "src/index.ts",
28
28
  "publishConfig": {
29
29
  "access": "public"
30
30
  },
31
31
  "optionalDependencies": {
32
- "@superdoc-dev/cli-darwin-arm64": "0.2.0-next.52",
33
- "@superdoc-dev/cli-darwin-x64": "0.2.0-next.52",
34
- "@superdoc-dev/cli-linux-arm64": "0.2.0-next.52",
35
- "@superdoc-dev/cli-windows-x64": "0.2.0-next.52",
36
- "@superdoc-dev/cli-linux-x64": "0.2.0-next.52"
32
+ "@superdoc-dev/cli-darwin-arm64": "0.2.0-next.54",
33
+ "@superdoc-dev/cli-darwin-x64": "0.2.0-next.54",
34
+ "@superdoc-dev/cli-linux-x64": "0.2.0-next.54",
35
+ "@superdoc-dev/cli-windows-x64": "0.2.0-next.54",
36
+ "@superdoc-dev/cli-linux-arm64": "0.2.0-next.54"
37
37
  },
38
38
  "scripts": {
39
39
  "dev": "bun run src/index.ts",