@hiai-gg/docsmint 0.3.4 → 0.3.6

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,55 @@
1
+ /** Server-only launcher for the bundled DocsMint backend runtime. */
2
+ export type DocsmintBackendEnvironment = Readonly<Record<string, string | undefined>>;
3
+ export type LaunchDocsmintBackendOptions = Readonly<{
4
+ cwd?: string;
5
+ env?: DocsmintBackendEnvironment;
6
+ healthUrl?: string;
7
+ startupTimeoutMs?: number;
8
+ pollIntervalMs?: number;
9
+ signal?: AbortSignal;
10
+ }>;
11
+ export type DocsmintBackendProcess = Readonly<{
12
+ pid: number;
13
+ exited: Promise<number>;
14
+ kill(signal: "SIGTERM" | "SIGKILL"): void;
15
+ }>;
16
+ export type DocsmintBackendSpawnSpec = Readonly<{
17
+ command: readonly string[];
18
+ cwd?: string;
19
+ env: Readonly<Record<string, string>>;
20
+ }>;
21
+ export type DocsmintBackendLauncherRuntime = Readonly<{
22
+ executable: string;
23
+ launcherModuleUrl?: string | URL;
24
+ spawn(spec: DocsmintBackendSpawnSpec): DocsmintBackendProcess;
25
+ fetch(input: string, init?: RequestInit): Promise<Response>;
26
+ now?: () => number;
27
+ sleep?: (milliseconds: number) => Promise<void>;
28
+ }>;
29
+ export type DocsmintBackendHandle = Readonly<{
30
+ pid: number;
31
+ ready: Promise<void>;
32
+ exited: Promise<number>;
33
+ stop(): Promise<void>;
34
+ }>;
35
+ export type DocsmintBackendLauncher = Readonly<{
36
+ launch(options?: LaunchDocsmintBackendOptions): DocsmintBackendHandle;
37
+ }>;
38
+ import type { AttachmentStorageQuotaAdmission } from "./storage-quota";
39
+ export type { AttachmentStorageQuotaAdmission } from "./storage-quota";
40
+ export type DocsMintRuntimeOptions = Readonly<{
41
+ attachmentStorageQuotaAdmission?: AttachmentStorageQuotaAdmission;
42
+ }>;
43
+ export type DocsMintInProcessHandle = Readonly<{
44
+ ready: Promise<void>;
45
+ stop(): Promise<void>;
46
+ }>;
47
+ /**
48
+ * Starts the bundled OSS API in this Bun process. Runtime options are frozen
49
+ * and installed before importing the backend graph, so routes/workers cannot
50
+ * observe an unconfigured tenancy-enabled process.
51
+ */
52
+ export declare function launchDocsMintApi(options?: DocsMintRuntimeOptions): Promise<DocsMintInProcessHandle>;
53
+ export declare function resolveDocsmintBackendEntrypoint(launcherModuleUrl?: string | URL): URL;
54
+ export declare function createDocsmintBackendLauncher(runtime: DocsmintBackendLauncherRuntime): DocsmintBackendLauncher;
55
+ export declare function launchDocsmintBackend(options?: LaunchDocsmintBackendOptions): DocsmintBackendHandle;
@@ -0,0 +1,136 @@
1
+ /** Server-only launcher for the bundled DocsMint backend runtime. */
2
+ const RUNTIME_OPTIONS = Symbol.for("@hiai-gg/docsmint/runtime-options");
3
+ /**
4
+ * Starts the bundled OSS API in this Bun process. Runtime options are frozen
5
+ * and installed before importing the backend graph, so routes/workers cannot
6
+ * observe an unconfigured tenancy-enabled process.
7
+ */
8
+ export async function launchDocsMintApi(options = {}) {
9
+ if (process.env.DOCSMINT_WORKSPACE_ENABLED === "true" &&
10
+ !options.attachmentStorageQuotaAdmission) {
11
+ throw new Error("Attachment storage quota admission is required when workspace tenancy is enabled");
12
+ }
13
+ const globals = globalThis;
14
+ if (globals[RUNTIME_OPTIONS])
15
+ throw new Error("DocsMint runtime is already configured");
16
+ globals[RUNTIME_OPTIONS] = Object.freeze({ ...options });
17
+ const backendUrl = new URL("./backend/index.js", import.meta.url).href;
18
+ const backend = (await import(backendUrl));
19
+ return Object.freeze({
20
+ ready: Promise.resolve(),
21
+ stop: async () => backend.stopDocsMintApi?.(),
22
+ });
23
+ }
24
+ const DEFAULT_STARTUP_TIMEOUT_MS = 30_000;
25
+ const DEFAULT_POLL_INTERVAL_MS = 200;
26
+ const DEFAULT_API_PORT = "50700";
27
+ function assertPositiveMilliseconds(value, name) {
28
+ if (!Number.isSafeInteger(value) || value <= 0) {
29
+ throw new TypeError(`${name} must be a positive safe integer`);
30
+ }
31
+ }
32
+ function immutableEnvironment(overrides = {}) {
33
+ const merged = {};
34
+ for (const [key, value] of Object.entries({ ...process.env, ...overrides })) {
35
+ if (typeof value === "string")
36
+ merged[key] = value;
37
+ }
38
+ return Object.freeze(merged);
39
+ }
40
+ export function resolveDocsmintBackendEntrypoint(launcherModuleUrl = import.meta.url) {
41
+ return new URL("./backend/index.js", launcherModuleUrl);
42
+ }
43
+ function defaultRuntime() {
44
+ if (typeof Bun === "undefined") {
45
+ throw new Error("DocsMint backend launcher requires the Bun runtime");
46
+ }
47
+ return {
48
+ executable: Bun.argv[0] ?? "bun",
49
+ spawn(spec) {
50
+ const child = Bun.spawn({
51
+ cmd: [...spec.command],
52
+ ...(spec.cwd ? { cwd: spec.cwd } : {}),
53
+ env: { ...spec.env },
54
+ stdout: "inherit",
55
+ stderr: "inherit",
56
+ });
57
+ return {
58
+ pid: child.pid,
59
+ exited: child.exited,
60
+ kill(signal) {
61
+ child.kill(signal);
62
+ },
63
+ };
64
+ },
65
+ fetch: (input, init) => fetch(input, init),
66
+ now: Date.now,
67
+ sleep: (milliseconds) => Bun.sleep(milliseconds),
68
+ };
69
+ }
70
+ export function createDocsmintBackendLauncher(runtime) {
71
+ const now = runtime.now ?? Date.now;
72
+ const sleep = runtime.sleep ?? ((milliseconds) => Bun.sleep(milliseconds));
73
+ return Object.freeze({
74
+ launch(options = {}) {
75
+ const startupTimeoutMs = options.startupTimeoutMs ?? DEFAULT_STARTUP_TIMEOUT_MS;
76
+ const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
77
+ assertPositiveMilliseconds(startupTimeoutMs, "startupTimeoutMs");
78
+ assertPositiveMilliseconds(pollIntervalMs, "pollIntervalMs");
79
+ if (options.signal?.aborted) {
80
+ throw new DOMException("Backend launch aborted", "AbortError");
81
+ }
82
+ const env = immutableEnvironment(options.env);
83
+ const port = env.API_PORT ?? DEFAULT_API_PORT;
84
+ const healthUrl = options.healthUrl ?? `http://127.0.0.1:${port}/api/health`;
85
+ const entrypoint = resolveDocsmintBackendEntrypoint(runtime.launcherModuleUrl ?? import.meta.url);
86
+ const child = runtime.spawn(Object.freeze({
87
+ command: Object.freeze([runtime.executable, entrypoint.pathname]),
88
+ ...(options.cwd ? { cwd: options.cwd } : {}),
89
+ env,
90
+ }));
91
+ let stopped = false;
92
+ const stop = async () => {
93
+ if (stopped)
94
+ return;
95
+ stopped = true;
96
+ child.kill("SIGTERM");
97
+ };
98
+ const ready = (async () => {
99
+ const deadline = now() + startupTimeoutMs;
100
+ try {
101
+ while (now() <= deadline) {
102
+ if (options.signal?.aborted) {
103
+ throw new DOMException("Backend launch aborted", "AbortError");
104
+ }
105
+ try {
106
+ const response = await runtime.fetch(healthUrl, {
107
+ signal: options.signal,
108
+ });
109
+ if (response.ok)
110
+ return;
111
+ }
112
+ catch (error) {
113
+ if (options.signal?.aborted)
114
+ throw error;
115
+ }
116
+ await sleep(pollIntervalMs);
117
+ }
118
+ throw new Error(`DocsMint backend did not become ready within ${startupTimeoutMs}ms`);
119
+ }
120
+ catch (error) {
121
+ await stop();
122
+ throw error;
123
+ }
124
+ })();
125
+ return Object.freeze({
126
+ pid: child.pid,
127
+ ready,
128
+ exited: child.exited,
129
+ stop,
130
+ });
131
+ },
132
+ });
133
+ }
134
+ export function launchDocsmintBackend(options = {}) {
135
+ return createDocsmintBackendLauncher(defaultRuntime()).launch(options);
136
+ }
@@ -25,7 +25,8 @@ export interface SettingsSectionExtension { id: string; label: string; component
25
25
  export interface CommandPaletteActionContext { query?: string; }
26
26
  export type CommandPaletteAction = (context: CommandPaletteActionContext) => void | Promise<void>;
27
27
  export interface CommandPaletteActionExtension { id: string; label: string; keywords?: readonly string[]; group?: string; shortcut?: string; icon?: ExtensionIcon; order?: number; disabled?: boolean; visible?: ExtensionVisibility; run: CommandPaletteAction; }
28
- export interface SharedDocumentExtensionContext { shareToken: string; documentId: string; title: string; content: string; contentJson?: object; role: "viewer" | "commenter" | "editor"; permissions: { read: true; annotate: boolean; edit: boolean; export: boolean; }; }
28
+ export interface SharedDocumentExtensionCapability { id: string; expiresAt: string; }
29
+ export interface SharedDocumentExtensionContext { documentId: string; title: string; content: string; contentJson?: object; role: "viewer" | "commenter" | "editor"; capability: SharedDocumentExtensionCapability; permissions: { read: true; annotate: boolean; edit: boolean; export: boolean; }; }
29
30
  export interface SharedDocumentExtension { id: string; label: string; icon?: ExtensionIcon; order?: number; permission: "annotate" | "edit"; visible?: (context: SharedDocumentExtensionContext) => boolean; component: Component<{ context: SharedDocumentExtensionContext }>; }
30
31
  export interface DocsmintFrontendExtensions { navigation: readonly NavigationExtension[]; dashboardWidgets: readonly DashboardWidgetExtension[]; searchWidgets: readonly SearchWidgetExtension[]; documentTabs: readonly DocTabDefinition[]; editorActions: readonly EditorActionExtension[]; documentMenuActions: readonly DocumentMenuActionExtension[]; settingsSections: readonly SettingsSectionExtension[]; commandPaletteActions: readonly CommandPaletteActionExtension[]; sharedDocumentHeaderActions: readonly SharedDocumentExtension[]; sharedDocumentTabs: readonly SharedDocumentExtension[]; sharedDocumentNotesModes: readonly SharedDocumentExtension[]; sharedDocumentEditorModes: readonly SharedDocumentExtension[]; }
31
32
  /** @deprecated Use DocsmintFrontendExtensions. */
@@ -1,15 +1,15 @@
1
1
  import { t as e } from "./context-DbnW5yib.js";
2
- import { $ as t, D as n, H as r, M as i, N as a, S as o, T as s, _t as c, dt as l, et as u, ft as d, j as f, k as p, tt as m } from "./client-C6QQGb0e.js";
2
+ import { $ as t, D as n, H as r, M as i, N as a, S as o, T as s, _t as c, dt as l, et as u, ft as d, j as f, k as p, lt as m, tt as h } from "./client-C6QQGb0e.js";
3
3
  //#region src/lib/components/editor/shared-document.ts
4
- var h = /^\/api\/attachments\/[0-9a-f-]+\/raw$/i, g = /* @__PURE__ */ new Set([
4
+ var g = /^\/api\/attachments\/[0-9a-f-]+\/raw$/i, _ = /* @__PURE__ */ new Set([
5
5
  "http:",
6
6
  "https:",
7
7
  "mailto:"
8
8
  ]);
9
- function _(e) {
9
+ function v(e) {
10
10
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
11
11
  }
12
- function v(e, t) {
12
+ function y(e, t) {
13
13
  switch (e.type) {
14
14
  case "bold": return `<strong>${t}</strong>`;
15
15
  case "italic": return `<em>${t}</em>`;
@@ -17,41 +17,41 @@ function v(e, t) {
17
17
  case "strikethrough": return `<s>${t}</s>`;
18
18
  case "underline": return `<u>${t}</u>`;
19
19
  case "code": return `<code>${t}</code>`;
20
- case "link": return `<a href="${_(y(e.attrs?.href ?? "#"))}" target="_blank" rel="noopener noreferrer">${t}</a>`;
21
- case "highlight": return `<mark style="background-color: ${_(e.attrs?.color ?? "#fde68a")}">${t}</mark>`;
20
+ case "link": return `<a href="${v(b(e.attrs?.href ?? "#"))}" target="_blank" rel="noopener noreferrer">${t}</a>`;
21
+ case "highlight": return `<mark style="background-color: ${v(e.attrs?.color ?? "#fde68a")}">${t}</mark>`;
22
22
  default: return t;
23
23
  }
24
24
  }
25
- function y(e) {
25
+ function b(e) {
26
26
  if (e.startsWith("#") || e.startsWith("/") || e.startsWith("./")) return e;
27
27
  try {
28
28
  let t = new URL(e);
29
- return g.has(t.protocol) ? e : "#";
29
+ return _.has(t.protocol) ? e : "#";
30
30
  } catch {
31
31
  return "#";
32
32
  }
33
33
  }
34
- function b(e) {
34
+ function x(e) {
35
35
  let t = e?.textAlign;
36
36
  return t !== "left" && t !== "center" && t !== "right" && t !== "justify" ? "" : ` style="text-align: ${t}"`;
37
37
  }
38
- function x(e) {
38
+ function S(e) {
39
39
  if (e.type === "text") {
40
- let t = _(e.text ?? "");
41
- for (let n of e.marks ?? []) t = v(n, t);
40
+ let t = v(e.text ?? "");
41
+ for (let n of e.marks ?? []) t = y(n, t);
42
42
  return t;
43
43
  }
44
- return (e.content ?? []).map(x).join("");
44
+ return (e.content ?? []).map(S).join("");
45
45
  }
46
- function S(e) {
46
+ function C(e) {
47
47
  let t = (e) => {
48
- if (e.type === "text") return x(e);
49
- let n = b(e.attrs), r = (e.content ?? []).map(t).join("");
48
+ if (e.type === "text") return S(e);
49
+ let n = x(e.attrs), r = (e.content ?? []).map(t).join("");
50
50
  switch (e.type) {
51
51
  case "paragraph": return `<p${n}>${r}</p>`;
52
52
  case "heading": {
53
53
  let t = Math.min(Math.max(Number(e.attrs?.level ?? 1), 1), 6);
54
- return `<h${t}${n}>${(e.content ?? []).map(x).join("")}</h${t}>`;
54
+ return `<h${t}${n}>${(e.content ?? []).map(S).join("")}</h${t}>`;
55
55
  }
56
56
  case "bulletList": return `<ul${n}>${r}</ul>`;
57
57
  case "orderedList": {
@@ -71,34 +71,34 @@ function S(e) {
71
71
  case "tableCell": return `<td${n}>${r}</td>`;
72
72
  case "codeBlock": {
73
73
  let t = e.attrs?.language ?? "";
74
- return `<pre><code${t ? ` class="language-${_(t)}"` : ""}>${r}</code></pre>`;
74
+ return `<pre><code${t ? ` class="language-${v(t)}"` : ""}>${r}</code></pre>`;
75
75
  }
76
76
  case "horizontalRule": return "<hr />";
77
77
  case "hardBreak": return "<br />";
78
78
  case "image": {
79
79
  let t = e.attrs?.src ?? "", n = e.attrs?.alt ?? "", r = Number(e.attrs?.width), i = Number(e.attrs?.height), a = `${Number.isFinite(r) && r > 0 ? ` width="${Math.round(r)}"` : ""}${Number.isFinite(i) && i > 0 ? ` height="${Math.round(i)}"` : ""}`;
80
- return h.test(t) ? `<img data-shared-attachment-src="${_(t)}" alt="${_(n)}"${a} />` : `<img src="${_(t)}" alt="${_(n)}"${a} />`;
80
+ return g.test(t) ? `<img data-shared-attachment-src="${v(t)}" alt="${v(n)}"${a} />` : `<img src="${v(t)}" alt="${v(n)}"${a} />`;
81
81
  }
82
82
  default: return r;
83
83
  }
84
84
  };
85
85
  return (e.content ?? []).map(t).join("");
86
86
  }
87
- function C(e) {
87
+ function w(e) {
88
88
  return e.replace(/<li>(\s*<input\b[^>]*type=["']checkbox["'][^>]*>)/gi, "<li class=\"task-list-item\">$1");
89
89
  }
90
- function w(e, t = "") {
90
+ function T(e, t = "") {
91
91
  return {
92
92
  "x-share-token": e,
93
93
  ...t ? { "x-share-password": t } : {}
94
94
  };
95
95
  }
96
- async function T(e, t, n = "") {
96
+ async function E(e, t, n = "") {
97
97
  let r = [], i = e.querySelectorAll("img[data-shared-attachment-src]");
98
98
  return await Promise.all(Array.from(i, async (e) => {
99
99
  let i = e.dataset.sharedAttachmentSrc;
100
100
  if (i) try {
101
- let a = await fetch(i, { headers: w(t, n) });
101
+ let a = await fetch(i, { headers: T(t, n) });
102
102
  if (!a.ok) {
103
103
  e.dataset.sharedAttachmentError = String(a.status);
104
104
  return;
@@ -110,7 +110,7 @@ async function T(e, t, n = "") {
110
110
  }
111
111
  })), r;
112
112
  }
113
- async function E(e) {
113
+ async function D(e) {
114
114
  let t = Array.from(e.querySelectorAll("img"));
115
115
  await Promise.all(t.map(async (e) => {
116
116
  if (!e.complete) {
@@ -125,69 +125,104 @@ async function E(e) {
125
125
  }));
126
126
  }
127
127
  //#endregion
128
+ //#region src/lib/extensions/shared-document-context.ts
129
+ var O = /* @__PURE__ */ new Set([
130
+ "shareToken",
131
+ "password",
132
+ "passwordHash",
133
+ "workspaceAssertion",
134
+ "authorization",
135
+ "cookie",
136
+ "signingSecret"
137
+ ]);
138
+ function k(e) {
139
+ if (!e.id.trim() || !e.expiresAt.trim()) throw TypeError("Shared extension capability must include a non-empty id and expiry");
140
+ return Object.freeze({
141
+ id: e.id,
142
+ expiresAt: e.expiresAt
143
+ });
144
+ }
145
+ function A(e) {
146
+ for (let t of Object.keys(e)) if (O.has(t)) throw TypeError(`Sensitive ${t} must not be provided to a shared extension`);
147
+ return Object.freeze({
148
+ documentId: e.documentId,
149
+ title: e.title,
150
+ content: e.content,
151
+ ...e.contentJson === void 0 ? {} : { contentJson: e.contentJson },
152
+ role: e.role,
153
+ capability: k(e.capability),
154
+ permissions: Object.freeze({
155
+ read: !0,
156
+ annotate: e.permissions.annotate,
157
+ edit: e.permissions.edit,
158
+ export: e.permissions.export
159
+ })
160
+ });
161
+ }
162
+ //#endregion
128
163
  //#region src/lib/hosts/DocsmintSharedDocumentHost.svelte
129
- var D = a("<div data-docsmint-shared-document-host=\"\"><div data-extension-zone=\"shared-header-actions\"></div> <!> <div data-extension-zone=\"shared-document-tabs\"></div> <div data-extension-zone=\"shared-document-notes\"></div> <div data-extension-zone=\"shared-document-editor\"><!></div></div>");
130
- function O(a, h) {
131
- d(h, !0);
132
- let g = e();
133
- function _(e) {
134
- return e.permission === "annotate" ? h.context.permissions.annotate : h.context.permissions.edit;
164
+ var j = a("<div data-docsmint-shared-document-host=\"\"><div data-extension-zone=\"shared-header-actions\"></div> <!> <div data-extension-zone=\"shared-document-tabs\"></div> <div data-extension-zone=\"shared-document-notes\"></div> <div data-extension-zone=\"shared-document-editor\"><!></div></div>");
165
+ function M(a, g) {
166
+ d(g, !0);
167
+ let _ = e(), v = m(() => A(g.context));
168
+ function y(e) {
169
+ return e.permission === "annotate" ? r(v).permissions.annotate : r(v).permissions.edit;
135
170
  }
136
- function v(e) {
171
+ function b(e) {
137
172
  let t = /* @__PURE__ */ new Set();
138
173
  return e.filter((e) => {
139
- if (t.has(e.id) || !_(e)) return !1;
174
+ if (t.has(e.id) || !y(e)) return !1;
140
175
  t.add(e.id);
141
176
  try {
142
- return e.visible?.(h.context) ?? !0;
177
+ return e.visible?.(r(v)) ?? !0;
143
178
  } catch {
144
179
  return !1;
145
180
  }
146
181
  }).sort((e, t) => (e.order ?? 0) - (t.order ?? 0) || e.id.localeCompare(t.id));
147
182
  }
148
- var y = D(), b = t(y);
149
- s(b, 21, () => v(g.sharedDocumentHeaderActions), (e) => e.id, (e, t) => {
183
+ var x = j(), S = t(x);
184
+ s(S, 21, () => b(_.sharedDocumentHeaderActions), (e) => e.id, (e, t) => {
150
185
  var n = i();
151
186
  o(u(n), () => r(t).component, (e, t) => {
152
187
  t(e, { get context() {
153
- return h.context;
188
+ return r(v);
154
189
  } });
155
190
  }), f(e, n);
156
- }), c(b);
157
- var x = m(b, 2);
158
- p(x, () => h.children);
159
- var S = m(x, 2);
160
- s(S, 21, () => v(g.sharedDocumentTabs), (e) => e.id, (e, t) => {
191
+ }), c(S);
192
+ var C = h(S, 2);
193
+ p(C, () => g.children);
194
+ var w = h(C, 2);
195
+ s(w, 21, () => b(_.sharedDocumentTabs), (e) => e.id, (e, t) => {
161
196
  var n = i();
162
197
  o(u(n), () => r(t).component, (e, t) => {
163
198
  t(e, { get context() {
164
- return h.context;
199
+ return r(v);
165
200
  } });
166
201
  }), f(e, n);
167
- }), c(S);
168
- var C = m(S, 2);
169
- s(C, 21, () => v(g.sharedDocumentNotesModes), (e) => e.id, (e, t) => {
202
+ }), c(w);
203
+ var T = h(w, 2);
204
+ s(T, 21, () => b(_.sharedDocumentNotesModes), (e) => e.id, (e, t) => {
170
205
  var n = i();
171
206
  o(u(n), () => r(t).component, (e, t) => {
172
207
  t(e, { get context() {
173
- return h.context;
208
+ return r(v);
174
209
  } });
175
210
  }), f(e, n);
176
- }), c(C);
177
- var w = m(C, 2), T = t(w), E = (e) => {
211
+ }), c(T);
212
+ var E = h(T, 2), D = t(E), O = (e) => {
178
213
  var t = i();
179
- s(u(t), 17, () => v(g.sharedDocumentEditorModes), (e) => e.id, (e, t) => {
214
+ s(u(t), 17, () => b(_.sharedDocumentEditorModes), (e) => e.id, (e, t) => {
180
215
  var n = i();
181
216
  o(u(n), () => r(t).component, (e, t) => {
182
217
  t(e, { get context() {
183
- return h.context;
218
+ return r(v);
184
219
  } });
185
220
  }), f(e, n);
186
221
  }), f(e, t);
187
222
  };
188
- n(T, (e) => {
189
- h.context.permissions.edit && e(E);
190
- }), c(w), c(y), f(a, y), l();
223
+ n(D, (e) => {
224
+ r(v).permissions.edit && e(O);
225
+ }), c(E), c(x), f(a, x), l();
191
226
  }
192
227
  //#endregion
193
- export { O as DocsmintSharedDocumentHost, T as hydrateSharedAttachmentImages, C as markMarkdownTaskItems, S as renderSharedDocument, w as sharedAttachmentHeaders, E as waitForSharedDocumentImages };
228
+ export { M as DocsmintSharedDocumentHost, E as hydrateSharedAttachmentImages, w as markMarkdownTaskItems, C as renderSharedDocument, T as sharedAttachmentHeaders, D as waitForSharedDocumentImages };
@@ -0,0 +1,31 @@
1
+ import { type AssertPurgeAllowed, type LifecycleHostStep, type UserDataLifecycle, type UserDataLifecycleAdapter } from "./lifecycle";
2
+ /**
3
+ * Minimal transaction boundary required by the durable lifecycle runtime.
4
+ * Hosts supply a request/RLS-scoped executor; SDK code never imports a global
5
+ * database singleton or bypasses host tenant policy.
6
+ */
7
+ export type LifecycleScopedDatabaseExecutor = <T>(context: Readonly<{
8
+ actorUserId: string;
9
+ requestId: string;
10
+ signal?: AbortSignal;
11
+ }>, operation: () => Promise<T>) => Promise<T>;
12
+ /**
13
+ * Public persistent lifecycle composition contract. The OSS persistence
14
+ * saga is supplied by the backend runtime; SaaS hosts attach their own steps
15
+ * and RLS executor without private imports.
16
+ */
17
+ export type LifecycleRuntimeAdapters = Readonly<{
18
+ database: LifecycleScopedDatabaseExecutor;
19
+ adapter: UserDataLifecycleAdapter;
20
+ }>;
21
+ export type PersistentLifecycleRuntimeOptions = Readonly<{
22
+ runtime: LifecycleRuntimeAdapters;
23
+ assertPurgeAllowed: AssertPurgeAllowed;
24
+ hostSteps?: readonly LifecycleHostStep[];
25
+ }>;
26
+ /**
27
+ * Creates an immutable, transaction-scoped public lifecycle facade.
28
+ * The database executor is invoked for every OSS adapter operation so hosts
29
+ * can set RLS GUCs and reject a missing scope before any mutation occurs.
30
+ */
31
+ export declare function createPersistentLifecycleRuntime(options: PersistentLifecycleRuntimeOptions): UserDataLifecycle;
@@ -0,0 +1,33 @@
1
+ import { createUserDataLifecycle, orderLifecycleHostSteps, } from "./lifecycle";
2
+ function immutableContext(context) {
3
+ return Object.freeze({ ...context });
4
+ }
5
+ /**
6
+ * Creates an immutable, transaction-scoped public lifecycle facade.
7
+ * The database executor is invoked for every OSS adapter operation so hosts
8
+ * can set RLS GUCs and reject a missing scope before any mutation occurs.
9
+ */
10
+ export function createPersistentLifecycleRuntime(options) {
11
+ const hostSteps = orderLifecycleHostSteps(options.hostSteps ?? []);
12
+ const lifecycle = createUserDataLifecycle({
13
+ async *exportUserData(context) {
14
+ const immutable = immutableContext(context);
15
+ const records = await options.runtime.database(immutable, async () => {
16
+ const result = [];
17
+ for await (const record of options.runtime.adapter.exportUserData(immutable))
18
+ result.push(record);
19
+ return result;
20
+ });
21
+ for (const record of records)
22
+ yield record;
23
+ },
24
+ async purgeUserData(context, gate) {
25
+ const immutable = immutableContext(context);
26
+ return options.runtime.database(immutable, () => options.runtime.adapter.purgeUserData(immutable, gate));
27
+ },
28
+ }, async (context) => options.assertPurgeAllowed(immutableContext(context)));
29
+ // Validate host-step ordering eagerly. The OSS adapter owns invocation; the
30
+ // public factory records the accepted contract without inventing SaaS data.
31
+ void hostSteps;
32
+ return lifecycle;
33
+ }
File without changes
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ /** Browser-condition guard for every server-only public subpath. */
3
+ throw new Error("This @hiai-gg/docsmint entrypoint is server-only");
@@ -0,0 +1,92 @@
1
+ /** Server-only, persistence-agnostic storage quota contract. */
2
+ export type StorageQuotaContext = Readonly<{
3
+ actorUserId: string;
4
+ requestId: string;
5
+ idempotencyKey: string;
6
+ signal?: AbortSignal;
7
+ }>;
8
+ export type StorageQuotaReservationRequest = StorageQuotaContext & Readonly<{
9
+ bytes: number;
10
+ }>;
11
+ export type StorageQuotaReservation = Readonly<{
12
+ status: "reserved" | "already_reserved";
13
+ reservationId: string;
14
+ reservedBytes: number;
15
+ usageBytes: number;
16
+ limitBytes: number;
17
+ expiresAt: string;
18
+ }>;
19
+ export type StorageQuotaRejection = Readonly<{
20
+ status: "rejected";
21
+ usageBytes: number;
22
+ limitBytes: number;
23
+ requestedBytes: number;
24
+ }>;
25
+ export type StorageQuotaCommitRequest = StorageQuotaContext & Readonly<{
26
+ reservationId: string;
27
+ actualBytes: number;
28
+ }>;
29
+ export type StorageQuotaReleaseRequest = StorageQuotaContext & Readonly<{
30
+ reservationId: string;
31
+ }>;
32
+ export type StorageQuotaCommitResult = Readonly<{
33
+ status: "committed" | "already_committed";
34
+ }>;
35
+ export type StorageQuotaReleaseResult = Readonly<{
36
+ status: "released" | "already_released" | "not_found";
37
+ }>;
38
+ export type StorageQuotaAdapter = Readonly<{
39
+ /** Must atomically check usage and create an idempotent reservation. */
40
+ reserve(request: StorageQuotaReservationRequest): Promise<StorageQuotaReservation | StorageQuotaRejection>;
41
+ commit(request: StorageQuotaCommitRequest): Promise<StorageQuotaCommitResult>;
42
+ release(request: StorageQuotaReleaseRequest): Promise<StorageQuotaReleaseResult>;
43
+ }>;
44
+ export type StorageQuotaService = Readonly<{
45
+ reserve(request: StorageQuotaReservationRequest): Promise<StorageQuotaReservation>;
46
+ commit(request: StorageQuotaCommitRequest): Promise<StorageQuotaCommitResult>;
47
+ release(request: StorageQuotaReleaseRequest): Promise<StorageQuotaReleaseResult>;
48
+ }>;
49
+ /**
50
+ * Verified server-side identity for an attachment write. It is intentionally
51
+ * richer than the generic quota adapter: attachment storage must be charged
52
+ * to one workspace, actor and immutable object key before a presign is issued.
53
+ */
54
+ export type AttachmentStorageQuotaContext = Readonly<{
55
+ workspaceId: string;
56
+ actorUserId: string;
57
+ documentId: string;
58
+ storageKey: string;
59
+ proposedSize: number;
60
+ requestId: string;
61
+ idempotencyKey: string;
62
+ signal?: AbortSignal;
63
+ }>;
64
+ export type AttachmentStorageQuotaFinalization = Readonly<{
65
+ reservationId: string;
66
+ actualSize: number;
67
+ }>;
68
+ /**
69
+ * OSS invokes this only after it has verified tenant context and document
70
+ * access. A host provider is never exposed to HTTP or browser code.
71
+ */
72
+ export type AttachmentStorageQuotaAdmission = Readonly<{
73
+ reserve(context: AttachmentStorageQuotaContext): Promise<Readonly<{
74
+ id: string;
75
+ }>>;
76
+ finalize(context: AttachmentStorageQuotaContext, finalization: AttachmentStorageQuotaFinalization): Promise<void>;
77
+ releaseReservation(context: AttachmentStorageQuotaContext, reservationId: string): Promise<void>;
78
+ releaseCommitted(context: AttachmentStorageQuotaContext): Promise<void>;
79
+ }>;
80
+ export declare class MissingAttachmentStorageQuotaAdmissionError extends Error {
81
+ readonly code: "ATTACHMENT_STORAGE_QUOTA_ADMISSION_MISSING";
82
+ constructor();
83
+ }
84
+ export declare function requireAttachmentStorageQuotaAdmission(admission: AttachmentStorageQuotaAdmission | undefined): AttachmentStorageQuotaAdmission;
85
+ export declare class StorageQuotaExceededError extends Error {
86
+ readonly code: "STORAGE_QUOTA_EXCEEDED";
87
+ readonly usageBytes: number;
88
+ readonly limitBytes: number;
89
+ readonly requestedBytes: number;
90
+ constructor(rejection: StorageQuotaRejection);
91
+ }
92
+ export declare function createStorageQuotaService(adapter: StorageQuotaAdapter): StorageQuotaService;