@hiai-gg/docsmint 0.3.0 → 0.3.2

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 (74) hide show
  1. package/README.md +26 -23
  2. package/{packages/sdk/dist → dist}/client.d.ts +16 -1
  3. package/{packages/sdk/dist → dist}/client.js +25 -1
  4. package/dist/frontend/DatePicker-BcMD49PV.js +275 -0
  5. package/dist/frontend/SettingsDialog-DiAyhzw0.js +4852 -0
  6. package/dist/frontend/ShareDialog-DaZf1raY.js +1317 -0
  7. package/dist/frontend/api/categories.d.ts +10 -0
  8. package/dist/frontend/api/categories.js +2 -0
  9. package/dist/frontend/api/documents.d.ts +10 -0
  10. package/dist/frontend/api/documents.js +2 -0
  11. package/dist/frontend/api/folders.d.ts +10 -0
  12. package/dist/frontend/api/folders.js +2 -0
  13. package/dist/frontend/api/settings.d.ts +8 -0
  14. package/dist/frontend/api/settings.js +2 -0
  15. package/dist/frontend/api/tags.d.ts +11 -0
  16. package/dist/frontend/api/tags.js +2 -0
  17. package/dist/frontend/categories-BE6ZpZl8.js +61 -0
  18. package/dist/frontend/client-B9hzI0W8.js +76 -0
  19. package/dist/frontend/client-C6QQGb0e.js +2766 -0
  20. package/dist/frontend/components/settings.d.ts +2 -0
  21. package/dist/frontend/components/settings.js +2 -0
  22. package/dist/frontend/components/sidebar.d.ts +2 -0
  23. package/dist/frontend/components/sidebar.js +4569 -0
  24. package/dist/frontend/context-DbnW5yib.js +31 -0
  25. package/dist/frontend/dashboard.d.ts +2 -0
  26. package/dist/frontend/dashboard.js +2152 -0
  27. package/dist/frontend/documents-DFVS9SsL.js +83 -0
  28. package/dist/frontend/extension.d.ts +2 -0
  29. package/dist/frontend/extension.js +10 -0
  30. package/dist/frontend/folders-CGe9diAG.js +91 -0
  31. package/dist/frontend/frontend.css +2 -0
  32. package/dist/frontend/i18n.d.ts +5 -0
  33. package/dist/frontend/i18n.js +8 -0
  34. package/dist/frontend/input-ieWIt8I7.js +257 -0
  35. package/dist/frontend/messages-CTGAsqFu.js +1106 -0
  36. package/dist/frontend/resolve-Bl6xmJl_.js +2068 -0
  37. package/dist/frontend/schemas-DwtKylCJ.js +3002 -0
  38. package/dist/frontend/search.d.ts +2 -0
  39. package/dist/frontend/search.js +1001 -0
  40. package/dist/frontend/settings-PCrOtied.js +1313 -0
  41. package/dist/frontend/shared-document.d.ts +3 -0
  42. package/dist/frontend/shared-document.js +193 -0
  43. package/dist/frontend/tag-C5xrg-bb.js +53 -0
  44. package/dist/frontend/tag-store.svelte-B3839FYq.js +6152 -0
  45. package/dist/frontend/tags-DVXTut0p.js +48 -0
  46. package/dist/frontend/theme.d.ts +4 -0
  47. package/dist/frontend/theme.js +8 -0
  48. package/dist/frontend/theme.svelte-ei0QxXa8.js +49 -0
  49. package/dist/index.d.ts +7 -0
  50. package/dist/index.js +3 -0
  51. package/dist/lifecycle.d.ts +90 -0
  52. package/dist/lifecycle.js +99 -0
  53. package/{packages/sdk/dist → dist}/types.d.ts +5 -0
  54. package/dist/workspace.d.ts +21 -0
  55. package/dist/workspace.js +66 -0
  56. package/package.json +70 -36
  57. package/packages/cli/src/index.ts +1 -1
  58. package/packages/db/src/schema.ts +41 -0
  59. package/packages/mcp-server/src/index.ts +1 -1
  60. package/frontend/src/lib/components/editor/shared-document.ts +0 -237
  61. package/frontend/src/lib/extensions/context.ts +0 -60
  62. package/frontend/src/lib/extensions/doc-tabs.ts +0 -18
  63. package/frontend/src/lib/extensions/resolve.ts +0 -48
  64. package/frontend/src/lib/extensions/types.ts +0 -202
  65. package/frontend/src/lib/hosts/DocsmintSharedDocumentHost.svelte +0 -65
  66. package/frontend/src/lib/hosts/HiaiDocsDashboardHost.svelte +0 -1007
  67. package/frontend/src/lib/hosts/HiaiDocsExtensionProvider.svelte +0 -20
  68. package/frontend/src/lib/hosts/HiaiDocsSearchHost.svelte +0 -996
  69. package/frontend/src/lib/hosts/index.ts +0 -25
  70. package/frontend/src/lib/index.ts +0 -65
  71. package/frontend/src/lib/stores/doc-tab-registry.svelte.ts +0 -68
  72. package/packages/sdk/dist/index.d.ts +0 -3
  73. package/packages/sdk/dist/index.js +0 -1
  74. /package/{packages/sdk/dist → dist}/types.js +0 -0
@@ -0,0 +1,48 @@
1
+ import { t as e } from "./client-B9hzI0W8.js";
2
+ import { a as t, i as n } from "./schemas-DwtKylCJ.js";
3
+ //#region src/lib/api/tags.ts
4
+ var r = n({
5
+ name: t().trim().min(1, "Name is required").max(50, "Name must be 50 characters or less"),
6
+ color: t().max(20, "Color must be 20 characters or less").optional()
7
+ }), i = n({
8
+ name: t().trim().min(1, "Name is required").max(50, "Name must be 50 characters or less").optional(),
9
+ color: t().max(20, "Color must be 20 characters or less").optional()
10
+ });
11
+ async function a(t) {
12
+ return e("/api/tags", {}, t);
13
+ }
14
+ function o(t) {
15
+ return e(`/api/tags/${t}`);
16
+ }
17
+ function s(t, n) {
18
+ let i = r.parse({
19
+ name: t,
20
+ color: n
21
+ });
22
+ return e("/api/tags", {
23
+ method: "POST",
24
+ body: JSON.stringify(i)
25
+ });
26
+ }
27
+ function c(t, n) {
28
+ let r = i.parse(n);
29
+ return e(`/api/tags/${t}`, {
30
+ method: "PATCH",
31
+ body: JSON.stringify(r)
32
+ });
33
+ }
34
+ function l(t) {
35
+ return e(`/api/tags/${t}`, { method: "DELETE" });
36
+ }
37
+ function u(r, i) {
38
+ let a = n({ tagId: t().uuid() }).parse({ tagId: i });
39
+ return e(`/api/documents/${r}/tags`, {
40
+ method: "POST",
41
+ body: JSON.stringify(a)
42
+ });
43
+ }
44
+ function d(t, n) {
45
+ return e(`/api/documents/${t}/tags/${n}`, { method: "DELETE" });
46
+ }
47
+ //#endregion
48
+ export { o as a, c, l as i, i as l, s as n, a as o, r, d as s, u as t };
@@ -0,0 +1,4 @@
1
+ export type ThemeMode = "light" | "dark" | "system";
2
+ export declare const theme: { readonly value: ThemeMode; readonly isDark: boolean };
3
+ export declare function setTheme(value: ThemeMode): void;
4
+ export declare function toggleTheme(): void;
@@ -0,0 +1,8 @@
1
+ import { t as e } from "./theme.svelte-ei0QxXa8.js";
2
+ //#region ../packages/sdk/frontend-entries/theme.ts
3
+ var t = e, n = e.set;
4
+ function r() {
5
+ n(t.value === "dark" ? "light" : "dark");
6
+ }
7
+ //#endregion
8
+ export { n as setTheme, t as theme, r as toggleTheme };
@@ -0,0 +1,49 @@
1
+ import { H as e, it as t, ot as n } from "./client-C6QQGb0e.js";
2
+ import { browser as r } from "$app/environment";
3
+ //#region src/lib/stores/theme.svelte.ts
4
+ var i = "hiai-docs-theme", a = /* @__PURE__ */ new Set();
5
+ function o() {
6
+ if (!r) return "system";
7
+ let e = localStorage.getItem(i);
8
+ return e === "light" || e === "dark" || e === "system" ? e : "system";
9
+ }
10
+ function s() {
11
+ return r ? window.matchMedia("(prefers-color-scheme: dark)").matches : !1;
12
+ }
13
+ function c(e) {
14
+ return e === "dark" ? !0 : e === "light" ? !1 : s();
15
+ }
16
+ function l(e) {
17
+ if (!r) return;
18
+ let t = c(e);
19
+ document.documentElement.classList.toggle("dark", t);
20
+ let n = document.querySelector("#app-favicon");
21
+ n && (n.href = t ? "/favicon_white.ico" : "/favicon.ico");
22
+ }
23
+ function u() {
24
+ for (let e of a) e();
25
+ }
26
+ var d = n("system"), f = n(!1), p = !1;
27
+ function m() {
28
+ if (!r || p) return;
29
+ p = !0, t(d, o(), !0), t(f, c(e(d)), !0), l(e(d));
30
+ let n = window.matchMedia("(prefers-color-scheme: dark)");
31
+ n.addEventListener("change", () => {
32
+ e(d) === "system" && (t(f, n.matches, !0), l("system"), u());
33
+ });
34
+ }
35
+ function h(e) {
36
+ t(d, e, !0), r && localStorage.setItem(i, e), t(f, c(e), !0), l(e), u();
37
+ }
38
+ var g = {
39
+ get value() {
40
+ return e(d);
41
+ },
42
+ get isDark() {
43
+ return e(f);
44
+ },
45
+ init: m,
46
+ set: h
47
+ };
48
+ //#endregion
49
+ export { g as t };
@@ -0,0 +1,7 @@
1
+ export type { DocsClientConfig } from "./client.js";
2
+ export { DocsApiError, DocsClient, DocsNetworkError, DocsTimeoutError, } from "./client.js";
3
+ export type * from "./types.js";
4
+ export { configureUserDataLifecycle, createUserDataLifecycle, encodeUserDataExportNdjson, exportUserData, purgeUserData, } from "./lifecycle.js";
5
+ export type { AssertPurgeAllowed, ExportUserDataContext, LifecycleHostStep, LifecycleOperationKind, LifecycleOperationStatus, PurgeUserDataContext, PurgeUserDataResult, UserDataExportRecord, UserDataLifecycle, UserDataLifecycleAdapter, } from "./lifecycle.js";
6
+ export { createDocsmintWorkspaceAssertion, verifyDocsmintWorkspaceAssertion, DOCSMINT_WORKSPACE_CONTEXT_HEADER, EXTERNAL_TENANT_CONTEXT_HEADER, } from "./workspace.js";
7
+ export type { DocsmintWorkspaceContext, WorkspaceAssertionOptions } from "./workspace.js";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { DocsApiError, DocsClient, DocsNetworkError, DocsTimeoutError, } from "./client.js";
2
+ export { configureUserDataLifecycle, createUserDataLifecycle, encodeUserDataExportNdjson, exportUserData, purgeUserData, } from "./lifecycle.js";
3
+ export { createDocsmintWorkspaceAssertion, verifyDocsmintWorkspaceAssertion, DOCSMINT_WORKSPACE_CONTEXT_HEADER, EXTERNAL_TENANT_CONTEXT_HEADER, } from "./workspace.js";
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Server-only account-data lifecycle contract.
3
+ *
4
+ * `actorUserId` is deliberately both the actor and the subject in 0.3.2. A
5
+ * trusted actor deleting another account requires a future, explicit
6
+ * `subjectUserId` contract; callers must not silently overload this field.
7
+ */
8
+ export type LifecycleOperationKind = "export" | "purge";
9
+ export type LifecycleOperationStatus = "pending" | "running" | "retryable" | "completed" | "rejected";
10
+ export type LifecycleReason = "account_deletion" | "privacy_request";
11
+ export type ExportUserDataContext = Readonly<{
12
+ actorUserId: string;
13
+ requestId: string;
14
+ idempotencyKey: string;
15
+ reason: LifecycleReason;
16
+ signal?: AbortSignal;
17
+ }>;
18
+ export type PurgeUserDataContext = Readonly<{
19
+ actorUserId: string;
20
+ requestId: string;
21
+ idempotencyKey: string;
22
+ reason: LifecycleReason;
23
+ signal?: AbortSignal;
24
+ }>;
25
+ export type UserDataExportRecord = Readonly<{
26
+ type: "manifest";
27
+ schemaVersion: 1;
28
+ exportId: string;
29
+ actorUserId: string;
30
+ generatedAt: string;
31
+ }> | Readonly<{
32
+ type: "data";
33
+ domain: string;
34
+ resourceType: string;
35
+ resourceId: string | null;
36
+ workspaceId: string | null;
37
+ payload: unknown;
38
+ }> | Readonly<{
39
+ type: "attachment";
40
+ attachmentId: string;
41
+ workspaceId: string | null;
42
+ filename: string;
43
+ contentType: string;
44
+ size: number;
45
+ sha256: string | null;
46
+ }> | Readonly<{
47
+ type: "complete";
48
+ recordCount: number;
49
+ checksum: string;
50
+ }>;
51
+ export type PurgeUserDataResult = Readonly<{
52
+ status: "completed" | "already_completed";
53
+ operationId: string;
54
+ deletedByDomain: Readonly<Record<string, number>>;
55
+ }>;
56
+ export type AssertPurgeAllowed = (ctx: PurgeUserDataContext) => Promise<Readonly<{
57
+ fenceToken: string;
58
+ }>>;
59
+ export type LifecycleHostStep = Readonly<{
60
+ id: string;
61
+ order: number;
62
+ export?: (ctx: ExportUserDataContext) => AsyncIterable<UserDataExportRecord>;
63
+ purge?: (ctx: PurgeUserDataContext) => Promise<Readonly<{
64
+ deletedCount: number;
65
+ }>>;
66
+ }>;
67
+ export interface UserDataLifecycleAdapter {
68
+ exportUserData(ctx: ExportUserDataContext): AsyncIterable<UserDataExportRecord>;
69
+ purgeUserData(ctx: PurgeUserDataContext, gate: Readonly<{
70
+ fenceToken: string;
71
+ }>): Promise<PurgeUserDataResult>;
72
+ }
73
+ export interface UserDataLifecycle {
74
+ exportUserData(ctx: ExportUserDataContext): AsyncIterable<UserDataExportRecord>;
75
+ purgeUserData(ctx: PurgeUserDataContext): Promise<PurgeUserDataResult>;
76
+ }
77
+ /** Install a server-only implementation during the host application's bootstrap. */
78
+ export declare function configureUserDataLifecycle(lifecycle: UserDataLifecycle): void;
79
+ export declare function exportUserData(ctx: ExportUserDataContext): AsyncIterable<UserDataExportRecord>;
80
+ export declare function purgeUserData(ctx: PurgeUserDataContext): Promise<PurgeUserDataResult>;
81
+ /** Construct a lifecycle facade around a durable host-owned implementation. */
82
+ export declare function createUserDataLifecycle(adapter: UserDataLifecycleAdapter, assertPurgeAllowed: AssertPurgeAllowed): UserDataLifecycle;
83
+ /** Validate and deterministically order registered host steps. */
84
+ export declare function orderLifecycleHostSteps(steps: readonly LifecycleHostStep[]): readonly LifecycleHostStep[];
85
+ /**
86
+ * Encode the typed stream as canonical NDJSON. The producer is authoritative
87
+ * for the terminal checksum, while the encoder verifies record sequencing so
88
+ * a partial or malformed stream is never presented as a valid export.
89
+ */
90
+ export declare function encodeUserDataExportNdjson(records: AsyncIterable<UserDataExportRecord>): ReadableStream<Uint8Array>;
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Server-only account-data lifecycle contract.
3
+ *
4
+ * `actorUserId` is deliberately both the actor and the subject in 0.3.2. A
5
+ * trusted actor deleting another account requires a future, explicit
6
+ * `subjectUserId` contract; callers must not silently overload this field.
7
+ */
8
+ let configuredLifecycle;
9
+ /** Install a server-only implementation during the host application's bootstrap. */
10
+ export function configureUserDataLifecycle(lifecycle) {
11
+ configuredLifecycle = lifecycle;
12
+ }
13
+ function requireLifecycle() {
14
+ if (!configuredLifecycle) {
15
+ throw new Error("DocsMint user-data lifecycle has not been configured");
16
+ }
17
+ return configuredLifecycle;
18
+ }
19
+ export function exportUserData(ctx) {
20
+ return requireLifecycle().exportUserData(Object.freeze({ ...ctx }));
21
+ }
22
+ export function purgeUserData(ctx) {
23
+ return requireLifecycle().purgeUserData(Object.freeze({ ...ctx }));
24
+ }
25
+ /** Construct a lifecycle facade around a durable host-owned implementation. */
26
+ export function createUserDataLifecycle(adapter, assertPurgeAllowed) {
27
+ return {
28
+ exportUserData: (ctx) => adapter.exportUserData(Object.freeze({ ...ctx })),
29
+ async purgeUserData(ctx) {
30
+ const immutableContext = Object.freeze({ ...ctx });
31
+ throwIfAborted(immutableContext.signal);
32
+ const gate = await assertPurgeAllowed(immutableContext);
33
+ if (!gate.fenceToken)
34
+ throw new Error("Purge gate returned an empty fence token");
35
+ throwIfAborted(immutableContext.signal);
36
+ return adapter.purgeUserData(immutableContext, gate);
37
+ },
38
+ };
39
+ }
40
+ /** Validate and deterministically order registered host steps. */
41
+ export function orderLifecycleHostSteps(steps) {
42
+ const ids = new Set();
43
+ for (const step of steps) {
44
+ if (!step.id || ids.has(step.id)) {
45
+ throw new Error(`Duplicate or empty lifecycle host step ID: ${step.id}`);
46
+ }
47
+ ids.add(step.id);
48
+ }
49
+ return Object.freeze([...steps].sort((a, b) => a.order - b.order || a.id.localeCompare(b.id)));
50
+ }
51
+ function throwIfAborted(signal) {
52
+ if (signal?.aborted)
53
+ throw signal.reason ?? new DOMException("Aborted", "AbortError");
54
+ }
55
+ /**
56
+ * Encode the typed stream as canonical NDJSON. The producer is authoritative
57
+ * for the terminal checksum, while the encoder verifies record sequencing so
58
+ * a partial or malformed stream is never presented as a valid export.
59
+ */
60
+ export function encodeUserDataExportNdjson(records) {
61
+ const encoder = new TextEncoder();
62
+ const iterator = records[Symbol.asyncIterator]();
63
+ let seenManifest = false;
64
+ let seenComplete = false;
65
+ let exhausted = false;
66
+ return new ReadableStream({
67
+ async pull(controller) {
68
+ try {
69
+ const next = await iterator.next();
70
+ if (next.done) {
71
+ exhausted = true;
72
+ if (!seenComplete)
73
+ throw new Error("Lifecycle export ended without complete record");
74
+ controller.close();
75
+ return;
76
+ }
77
+ const record = next.value;
78
+ if (!seenManifest) {
79
+ if (record.type !== "manifest")
80
+ throw new Error("Lifecycle export must begin with manifest");
81
+ seenManifest = true;
82
+ }
83
+ else if (seenComplete) {
84
+ throw new Error("Lifecycle export emitted a record after complete");
85
+ }
86
+ if (record.type === "complete")
87
+ seenComplete = true;
88
+ controller.enqueue(encoder.encode(`${JSON.stringify(record)}\n`));
89
+ }
90
+ catch (error) {
91
+ controller.error(error);
92
+ }
93
+ },
94
+ async cancel(reason) {
95
+ if (!exhausted)
96
+ await iterator.return?.(reason);
97
+ },
98
+ });
99
+ }
@@ -65,6 +65,11 @@ export interface DocsDocumentListResponse {
65
65
  page: number;
66
66
  limit: number;
67
67
  }
68
+ /** Opaque, scope-bound cursor returned by the bounded document listing API. */
69
+ export interface DocsDocumentCursorPage {
70
+ items: DocsDocumentListItem[];
71
+ nextCursor: string | null;
72
+ }
68
73
  export interface DocsFolder {
69
74
  id: string;
70
75
  ownerId: string;
@@ -0,0 +1,21 @@
1
+ /** Public, server-only workspace assertion contract. */
2
+ export declare const DOCSMINT_WORKSPACE_CONTEXT_HEADER = "x-docsmint-workspace-context";
3
+ /** @deprecated Compatibility header accepted only during 0.3.x. */
4
+ export declare const EXTERNAL_TENANT_CONTEXT_HEADER = "x-hiai-tenant-context";
5
+ export type DocsmintWorkspaceContext = Readonly<{
6
+ actorUserId: string;
7
+ workspaceId: string;
8
+ actorRole: "owner" | "admin" | "editor" | "viewer";
9
+ issuedAt: number;
10
+ expiresAt: number;
11
+ issuer: string;
12
+ }>;
13
+ export type WorkspaceAssertionOptions = Readonly<{
14
+ secret: string;
15
+ issuer: string;
16
+ nowSeconds?: number;
17
+ clockSkewSeconds?: number;
18
+ maxTtlSeconds?: number;
19
+ }>;
20
+ export declare function createDocsmintWorkspaceAssertion(context: DocsmintWorkspaceContext, secret: string): Promise<string>;
21
+ export declare function verifyDocsmintWorkspaceAssertion(assertion: string, options: WorkspaceAssertionOptions): Promise<DocsmintWorkspaceContext>;
@@ -0,0 +1,66 @@
1
+ /** Public, server-only workspace assertion contract. */
2
+ export const DOCSMINT_WORKSPACE_CONTEXT_HEADER = "x-docsmint-workspace-context";
3
+ /** @deprecated Compatibility header accepted only during 0.3.x. */
4
+ export const EXTERNAL_TENANT_CONTEXT_HEADER = "x-hiai-tenant-context";
5
+ const encoder = new TextEncoder();
6
+ const decoder = new TextDecoder();
7
+ const MAX_TTL_SECONDS = 300;
8
+ function toBase64Url(bytes) {
9
+ let binary = "";
10
+ for (const byte of bytes)
11
+ binary += String.fromCharCode(byte);
12
+ return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
13
+ }
14
+ function fromBase64Url(value) {
15
+ if (!/^[A-Za-z0-9_-]+$/.test(value))
16
+ throw new Error("Invalid base64url");
17
+ const padded = value.replaceAll("-", "+").replaceAll("_", "/") + "=".repeat((4 - (value.length % 4)) % 4);
18
+ const binary = atob(padded);
19
+ return Uint8Array.from(binary, (character) => character.charCodeAt(0));
20
+ }
21
+ async function sign(payload, secret) {
22
+ const key = await crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
23
+ return toBase64Url(new Uint8Array(await crypto.subtle.sign("HMAC", key, encoder.encode(payload))));
24
+ }
25
+ function assertContext(value) {
26
+ if (!value || typeof value !== "object")
27
+ throw new Error("Invalid workspace assertion payload");
28
+ const context = value;
29
+ if (typeof context.actorUserId !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(context.actorUserId))
30
+ throw new Error("Invalid actorUserId");
31
+ if (typeof context.workspaceId !== "string" || !context.workspaceId.trim() || context.workspaceId.trim() !== context.workspaceId || context.workspaceId.length > 128)
32
+ throw new Error("Invalid workspaceId");
33
+ if (!["owner", "admin", "editor", "viewer"].includes(context.actorRole))
34
+ throw new Error("Invalid actorRole");
35
+ if (!Number.isFinite(context.issuedAt) || !Number.isFinite(context.expiresAt) || typeof context.issuer !== "string" || !context.issuer)
36
+ throw new Error("Invalid workspace assertion timestamps or issuer");
37
+ }
38
+ export async function createDocsmintWorkspaceAssertion(context, secret) {
39
+ assertContext(context);
40
+ const payload = toBase64Url(encoder.encode(JSON.stringify(context)));
41
+ return `${payload}.${await sign(payload, secret)}`;
42
+ }
43
+ export async function verifyDocsmintWorkspaceAssertion(assertion, options) {
44
+ const [payload, signature, ...extra] = assertion.split(".");
45
+ if (!payload || !signature || extra.length)
46
+ throw new Error("Invalid workspace assertion format");
47
+ const key = await crypto.subtle.importKey("raw", encoder.encode(options.secret), { name: "HMAC", hash: "SHA-256" }, false, ["verify"]);
48
+ const valid = await crypto.subtle.verify("HMAC", key, fromBase64Url(signature), encoder.encode(payload));
49
+ if (!valid)
50
+ throw new Error("Invalid workspace assertion signature");
51
+ let context;
52
+ try {
53
+ context = JSON.parse(decoder.decode(fromBase64Url(payload)));
54
+ }
55
+ catch {
56
+ throw new Error("Invalid workspace assertion payload");
57
+ }
58
+ assertContext(context);
59
+ if (context.issuer !== options.issuer)
60
+ throw new Error("Invalid workspace assertion issuer");
61
+ const now = options.nowSeconds ?? Math.floor(Date.now() / 1000);
62
+ const skew = options.clockSkewSeconds ?? 5;
63
+ if (context.issuedAt > now + skew || context.expiresAt <= now - skew || context.expiresAt <= context.issuedAt || context.expiresAt - context.issuedAt > (options.maxTtlSeconds ?? MAX_TTL_SECONDS))
64
+ throw new Error("Invalid workspace assertion lifetime");
65
+ return Object.freeze({ ...context });
66
+ }
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@hiai-gg/docsmint",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "type": "module",
5
+ "browser": {
6
+ "./dist/lifecycle.js": false,
7
+ "./dist/workspace.js": false
8
+ },
5
9
  "license": "Apache-2.0",
6
10
  "description": "The lightweight, AI-native self-hosted knowledge base — built-in RAG, hybrid semantic search, and a clean REST API for AI agents.",
7
11
  "repository": {
@@ -31,36 +35,78 @@
31
35
  "outline-alternative",
32
36
  "docmost-alternative"
33
37
  ],
34
- "main": "./packages/sdk/dist/index.js",
35
- "types": "./packages/sdk/dist/index.d.ts",
38
+ "main": "./dist/index.js",
39
+ "types": "./dist/index.d.ts",
36
40
  "exports": {
37
41
  ".": {
38
- "import": "./packages/sdk/dist/index.js",
39
- "types": "./packages/sdk/dist/index.d.ts"
42
+ "import": "./dist/index.js",
43
+ "types": "./dist/index.d.ts"
40
44
  },
41
45
  "./sdk": {
42
- "import": "./packages/sdk/dist/index.js",
43
- "types": "./packages/sdk/dist/index.d.ts"
46
+ "import": "./dist/index.js",
47
+ "types": "./dist/index.d.ts"
48
+ },
49
+ "./lifecycle": {
50
+ "browser": "./dist/server-only-browser-entry.js",
51
+ "import": "./dist/lifecycle.js",
52
+ "types": "./dist/lifecycle.d.ts"
53
+ },
54
+ "./workspace": {
55
+ "browser": "./dist/server-only-browser-entry.js",
56
+ "import": "./dist/workspace.js",
57
+ "types": "./dist/workspace.d.ts"
44
58
  },
45
- "./frontend": {
46
- "import": "./frontend/src/lib/index.ts",
47
- "types": "./frontend/src/lib/index.ts"
59
+ "./frontend/dashboard": {
60
+ "import": "./dist/frontend/dashboard.js",
61
+ "types": "./dist/frontend/dashboard.d.ts"
62
+ },
63
+ "./frontend/search": {
64
+ "import": "./dist/frontend/search.js",
65
+ "types": "./dist/frontend/search.d.ts"
48
66
  },
49
67
  "./frontend/shared-document": {
50
- "import": "./frontend/src/lib/components/editor/shared-document.ts",
51
- "types": "./frontend/src/lib/components/editor/shared-document.ts"
68
+ "import": "./dist/frontend/shared-document.js",
69
+ "types": "./dist/frontend/shared-document.d.ts"
70
+ },
71
+ "./frontend/extension": {
72
+ "import": "./dist/frontend/extension.js",
73
+ "types": "./dist/frontend/extension.d.ts"
74
+ },
75
+ "./frontend/api/categories": {
76
+ "import": "./dist/frontend/api/categories.js",
77
+ "types": "./dist/frontend/api/categories.d.ts"
78
+ },
79
+ "./frontend/api/documents": {
80
+ "import": "./dist/frontend/api/documents.js",
81
+ "types": "./dist/frontend/api/documents.d.ts"
82
+ },
83
+ "./frontend/api/folders": {
84
+ "import": "./dist/frontend/api/folders.js",
85
+ "types": "./dist/frontend/api/folders.d.ts"
86
+ },
87
+ "./frontend/api/tags": {
88
+ "import": "./dist/frontend/api/tags.js",
89
+ "types": "./dist/frontend/api/tags.d.ts"
90
+ },
91
+ "./frontend/api/settings": {
92
+ "import": "./dist/frontend/api/settings.js",
93
+ "types": "./dist/frontend/api/settings.d.ts"
94
+ },
95
+ "./frontend/components/sidebar": {
96
+ "import": "./dist/frontend/components/sidebar.js",
97
+ "types": "./dist/frontend/components/sidebar.d.ts"
52
98
  },
53
- "./frontend/extensions": {
54
- "import": "./frontend/src/lib/extensions/types.ts",
55
- "types": "./frontend/src/lib/extensions/types.ts"
99
+ "./frontend/components/settings": {
100
+ "import": "./dist/frontend/components/settings.js",
101
+ "types": "./dist/frontend/components/settings.d.ts"
56
102
  },
57
- "./frontend/hosts": {
58
- "import": "./frontend/src/lib/hosts/index.ts",
59
- "types": "./frontend/src/lib/hosts/index.ts"
103
+ "./frontend/theme": {
104
+ "import": "./dist/frontend/theme.js",
105
+ "types": "./dist/frontend/theme.d.ts"
60
106
  },
61
- "./frontend/legacy/doc-tab-registry": {
62
- "import": "./frontend/src/lib/stores/doc-tab-registry.svelte.ts",
63
- "types": "./frontend/src/lib/stores/doc-tab-registry.svelte.ts"
107
+ "./frontend/i18n": {
108
+ "import": "./dist/frontend/i18n.js",
109
+ "types": "./dist/frontend/i18n.d.ts"
64
110
  },
65
111
  "./schema": {
66
112
  "import": "./packages/db/src/schema.ts",
@@ -98,12 +144,7 @@
98
144
  "hiai-docs-mcp": "./packages/mcp-server/src/index.ts"
99
145
  },
100
146
  "files": [
101
- "packages/sdk/dist/index.js",
102
- "packages/sdk/dist/index.d.ts",
103
- "packages/sdk/dist/client.js",
104
- "packages/sdk/dist/client.d.ts",
105
- "packages/sdk/dist/types.js",
106
- "packages/sdk/dist/types.d.ts",
147
+ "dist",
107
148
  "packages/db/src/index.ts",
108
149
  "packages/db/src/client.ts",
109
150
  "packages/db/src/schema.ts",
@@ -120,14 +161,6 @@
120
161
  "backend/src/lib/redis-factory.ts",
121
162
  "backend/src/lib/storage-factory.ts",
122
163
  "backend/src/lib/logger.ts",
123
- "frontend/src/lib/index.ts",
124
- "frontend/src/lib/extensions/context.ts",
125
- "frontend/src/lib/extensions/doc-tabs.ts",
126
- "frontend/src/lib/extensions/resolve.ts",
127
- "frontend/src/lib/extensions/types.ts",
128
- "frontend/src/lib/hosts",
129
- "frontend/src/lib/stores/doc-tab-registry.svelte.ts",
130
- "frontend/src/lib/components/editor/shared-document.ts",
131
164
  "README.md",
132
165
  "LICENSE"
133
166
  ],
@@ -156,6 +189,7 @@
156
189
  "ioredis": "^5.4.0",
157
190
  "@aws-sdk/client-s3": "^3.0.0",
158
191
  "pino": "^10.0.0",
192
+ "@sveltejs/kit": "^2.68.0",
159
193
  "svelte": "^5.56.4"
160
194
  },
161
195
  "peerDependenciesMeta": {
@@ -175,4 +209,4 @@
175
209
  "optional": true
176
210
  }
177
211
  }
178
- }
212
+ }
@@ -24,7 +24,7 @@ import { registerSearch } from "./commands/search.js";
24
24
  import { registerSnapshot } from "./commands/snapshot.js";
25
25
  import { registerUpdate } from "./commands/update.js";
26
26
 
27
- const VERSION = "0.3.0";
27
+ const VERSION = "0.3.2";
28
28
 
29
29
  const program = new Command();
30
30
  program
@@ -56,6 +56,17 @@ export const pipelineStatusEnum = pgEnum("pipeline_status", [
56
56
  "skipped",
57
57
  "cancelled",
58
58
  ]);
59
+ export const lifecycleOperationKindEnum = pgEnum("lifecycle_operation_kind", [
60
+ "export",
61
+ "purge",
62
+ ]);
63
+ export const lifecycleOperationStatusEnum = pgEnum("lifecycle_operation_status", [
64
+ "pending",
65
+ "running",
66
+ "retryable",
67
+ "completed",
68
+ "rejected",
69
+ ]);
59
70
 
60
71
  // ============================================
61
72
  // users — managed by Better Auth
@@ -680,6 +691,36 @@ export const auditLog = pgTable(
680
691
  ]
681
692
  );
682
693
 
694
+ // Durable, server-only lifecycle saga state. This table records only safe
695
+ // metadata and counts; raw fence tokens, documents, secrets, and provider
696
+ // errors are intentionally excluded from the schema.
697
+ export const lifecycleOperations = pgTable(
698
+ "lifecycle_operations",
699
+ {
700
+ id: uuid("id").primaryKey().defaultRandom(),
701
+ actorUserId: uuid("actor_user_id").notNull().references(() => users.id, { onDelete: "restrict" }),
702
+ idempotencyKey: text("idempotency_key").notNull(),
703
+ operationKind: lifecycleOperationKindEnum("operation_kind").notNull(),
704
+ status: lifecycleOperationStatusEnum("status").notNull().default("pending"),
705
+ leaseOwner: text("lease_owner"),
706
+ leaseExpiresAt: timestamp("lease_expires_at"),
707
+ fenceTokenHash: text("fence_token_hash"),
708
+ completedSteps: jsonb("completed_steps").notNull().default("[]"),
709
+ terminalResult: jsonb("terminal_result"),
710
+ safeErrorCode: text("safe_error_code"),
711
+ attemptCount: integer("attempt_count").notNull().default(0),
712
+ createdAt: timestamp("created_at").defaultNow().notNull(),
713
+ updatedAt: timestamp("updated_at").defaultNow().notNull(),
714
+ completedAt: timestamp("completed_at"),
715
+ },
716
+ (table) => [
717
+ uniqueIndex("lifecycle_operations_actor_idempotency_idx").on(table.actorUserId, table.idempotencyKey),
718
+ index("lifecycle_operations_status_lease_idx").on(table.status, table.leaseExpiresAt),
719
+ index("lifecycle_operations_actor_idx").on(table.actorUserId),
720
+ index("lifecycle_operations_retryable_idx").on(table.status).where(sql`${table.status} = 'retryable'`),
721
+ ],
722
+ );
723
+
683
724
  export const versionRelations = relations(versions, ({ one }) => ({
684
725
  document: one(documents, {
685
726
  fields: [versions.documentId],
@@ -35,7 +35,7 @@ interface McpToolResult {
35
35
 
36
36
  const server = new McpServer({
37
37
  name: "hiai-docs",
38
- version: "0.3.0",
38
+ version: "0.3.2",
39
39
  });
40
40
 
41
41
  /**