@opengeni/artifact-tool 0.2.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.
- package/LICENSE +190 -0
- package/README.md +104 -0
- package/dist/bounded-zip.d.ts +28 -0
- package/dist/chunk-4BSENMAE.js +39 -0
- package/dist/chunk-4BSENMAE.js.map +1 -0
- package/dist/chunk-4FRFNNBM.js +276 -0
- package/dist/chunk-4FRFNNBM.js.map +1 -0
- package/dist/chunk-5XIA6KHZ.js +49 -0
- package/dist/chunk-5XIA6KHZ.js.map +1 -0
- package/dist/chunk-66CT2ZRP.js +2229 -0
- package/dist/chunk-66CT2ZRP.js.map +1 -0
- package/dist/chunk-72SYDU4O.js +319 -0
- package/dist/chunk-72SYDU4O.js.map +1 -0
- package/dist/chunk-B2ZJRZDA.js +279 -0
- package/dist/chunk-B2ZJRZDA.js.map +1 -0
- package/dist/chunk-DCTLTNPW.js +2458 -0
- package/dist/chunk-DCTLTNPW.js.map +1 -0
- package/dist/chunk-FGZM4FQ6.js +473 -0
- package/dist/chunk-FGZM4FQ6.js.map +1 -0
- package/dist/chunk-HTJBLBDE.js +591 -0
- package/dist/chunk-HTJBLBDE.js.map +1 -0
- package/dist/chunk-IYFXZQ34.js +5306 -0
- package/dist/chunk-IYFXZQ34.js.map +1 -0
- package/dist/chunk-J6N7IR44.js +172 -0
- package/dist/chunk-J6N7IR44.js.map +1 -0
- package/dist/chunk-N2IHNWBS.js +1002 -0
- package/dist/chunk-N2IHNWBS.js.map +1 -0
- package/dist/chunk-NAG44K67.js +549 -0
- package/dist/chunk-NAG44K67.js.map +1 -0
- package/dist/chunk-O24KTEGP.js +33 -0
- package/dist/chunk-O24KTEGP.js.map +1 -0
- package/dist/chunk-PI2AZVGK.js +2074 -0
- package/dist/chunk-PI2AZVGK.js.map +1 -0
- package/dist/chunk-Q2FFHEY7.js +40 -0
- package/dist/chunk-Q2FFHEY7.js.map +1 -0
- package/dist/chunk-RBGQADXA.js +57 -0
- package/dist/chunk-RBGQADXA.js.map +1 -0
- package/dist/chunk-U5G2BKM2.js +613 -0
- package/dist/chunk-U5G2BKM2.js.map +1 -0
- package/dist/chunk-VOJHMSM3.js +39 -0
- package/dist/chunk-VOJHMSM3.js.map +1 -0
- package/dist/chunk-VUNV25KB.js +16 -0
- package/dist/chunk-VUNV25KB.js.map +1 -0
- package/dist/chunk-XIFUNVBO.js +101 -0
- package/dist/chunk-XIFUNVBO.js.map +1 -0
- package/dist/chunk-ZCJYRJGB.js +489 -0
- package/dist/chunk-ZCJYRJGB.js.map +1 -0
- package/dist/chunk-ZZXPEE4S.js +2260 -0
- package/dist/chunk-ZZXPEE4S.js.map +1 -0
- package/dist/document-docx-api.d.ts +49 -0
- package/dist/document-docx-codec.d.ts +9 -0
- package/dist/document-docx-codec.js +4765 -0
- package/dist/document-docx-codec.js.map +1 -0
- package/dist/document-docx-import.d.ts +261 -0
- package/dist/document-docx-packer.d.ts +18 -0
- package/dist/document-docx-state.d.ts +12 -0
- package/dist/document-render.d.ts +3 -0
- package/dist/document-render.js +492 -0
- package/dist/document-render.js.map +1 -0
- package/dist/document.d.ts +432 -0
- package/dist/document.js +37 -0
- package/dist/document.js.map +1 -0
- package/dist/errors.d.ts +23 -0
- package/dist/file-blob.d.ts +11 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +186 -0
- package/dist/index.js.map +1 -0
- package/dist/inspection.d.ts +12 -0
- package/dist/kernel.d.ts +101 -0
- package/dist/lib-GQLPACFK.js +9746 -0
- package/dist/lib-GQLPACFK.js.map +1 -0
- package/dist/materializer-cli-entry.d.ts +2 -0
- package/dist/materializer-cli-entry.js +29 -0
- package/dist/materializer-cli-entry.js.map +1 -0
- package/dist/materializer-cli.d.ts +24 -0
- package/dist/materializer-cli.js +24 -0
- package/dist/materializer-cli.js.map +1 -0
- package/dist/native-raster.d.ts +10 -0
- package/dist/native.d.ts +129 -0
- package/dist/native.js +31 -0
- package/dist/native.js.map +1 -0
- package/dist/office-import.d.ts +48 -0
- package/dist/office-import.js +154 -0
- package/dist/office-import.js.map +1 -0
- package/dist/presentation-pptx-api.d.ts +60 -0
- package/dist/presentation-pptx-import.d.ts +5 -0
- package/dist/presentation-pptx-state-digest-765CZZED.js +10 -0
- package/dist/presentation-pptx-state-digest-765CZZED.js.map +1 -0
- package/dist/presentation-pptx-state-digest.d.ts +3 -0
- package/dist/presentation-pptx-state.d.ts +4 -0
- package/dist/presentation-pptx-xml.d.ts +29 -0
- package/dist/presentation-pptx.d.ts +5 -0
- package/dist/presentation-pptx.js +2365 -0
- package/dist/presentation-pptx.js.map +1 -0
- package/dist/presentation-render.d.ts +29 -0
- package/dist/presentation-render.js +375 -0
- package/dist/presentation-render.js.map +1 -0
- package/dist/presentation.d.ts +565 -0
- package/dist/presentation.js +71 -0
- package/dist/presentation.js.map +1 -0
- package/dist/production-composite.d.ts +68 -0
- package/dist/production-document.d.ts +2 -0
- package/dist/production-document.js +176 -0
- package/dist/production-document.js.map +1 -0
- package/dist/production-native-codecs.d.ts +94 -0
- package/dist/production-presentation.d.ts +2 -0
- package/dist/production-presentation.js +176 -0
- package/dist/production-presentation.js.map +1 -0
- package/dist/production-sha256.d.ts +2 -0
- package/dist/production-spreadsheet-native.d.ts +12 -0
- package/dist/production-spreadsheet.d.ts +2 -0
- package/dist/production-spreadsheet.js +176 -0
- package/dist/production-spreadsheet.js.map +1 -0
- package/dist/production.d.ts +96 -0
- package/dist/raster-image.d.ts +34 -0
- package/dist/reference.d.ts +17 -0
- package/dist/reference.js +574 -0
- package/dist/reference.js.map +1 -0
- package/dist/runtime-cli-entry.d.ts +2 -0
- package/dist/runtime-cli-entry.js +21 -0
- package/dist/runtime-cli-entry.js.map +1 -0
- package/dist/runtime-cli.d.ts +76 -0
- package/dist/runtime-cli.js +26 -0
- package/dist/runtime-cli.js.map +1 -0
- package/dist/runtime-development.d.ts +73 -0
- package/dist/runtime-development.js +30 -0
- package/dist/runtime-development.js.map +1 -0
- package/dist/runtime-receipt.d.ts +19 -0
- package/dist/runtime.d.ts +355 -0
- package/dist/runtime.js +42 -0
- package/dist/runtime.js.map +1 -0
- package/dist/snapshot.d.ts +26 -0
- package/dist/snapshot.js +61 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/spreadsheet-address.d.ts +18 -0
- package/dist/spreadsheet-file.d.ts +17 -0
- package/dist/spreadsheet-formula.d.ts +67 -0
- package/dist/spreadsheet-image.d.ts +17 -0
- package/dist/spreadsheet-render.d.ts +5 -0
- package/dist/spreadsheet-render.js +752 -0
- package/dist/spreadsheet-render.js.map +1 -0
- package/dist/spreadsheet-types.d.ts +155 -0
- package/dist/spreadsheet-xlsx-api.d.ts +88 -0
- package/dist/spreadsheet-xlsx-codec.d.ts +16 -0
- package/dist/spreadsheet-xlsx-codec.js +16 -0
- package/dist/spreadsheet-xlsx-codec.js.map +1 -0
- package/dist/spreadsheet-xlsx-state.d.ts +16 -0
- package/dist/spreadsheet.d.ts +724 -0
- package/dist/spreadsheet.js +67 -0
- package/dist/spreadsheet.js.map +1 -0
- package/package.json +129 -0
- package/src/bounded-zip.ts +450 -0
- package/src/document-docx-api.ts +64 -0
- package/src/document-docx-codec.ts +2253 -0
- package/src/document-docx-import.ts +3814 -0
- package/src/document-docx-packer.ts +72 -0
- package/src/document-docx-state.ts +59 -0
- package/src/document-render.ts +615 -0
- package/src/document.ts +2800 -0
- package/src/errors.ts +45 -0
- package/src/file-blob.ts +43 -0
- package/src/index.ts +4 -0
- package/src/inspection.ts +50 -0
- package/src/kernel.ts +525 -0
- package/src/materializer-cli-entry.ts +11 -0
- package/src/materializer-cli.ts +743 -0
- package/src/native-raster.ts +141 -0
- package/src/native.ts +390 -0
- package/src/office-import.ts +243 -0
- package/src/presentation-pptx-api.ts +107 -0
- package/src/presentation-pptx-import.ts +2065 -0
- package/src/presentation-pptx-state-digest.ts +46 -0
- package/src/presentation-pptx-state.ts +18 -0
- package/src/presentation-pptx-xml.ts +473 -0
- package/src/presentation-pptx.ts +447 -0
- package/src/presentation-render.ts +532 -0
- package/src/presentation.ts +2599 -0
- package/src/production-composite.ts +805 -0
- package/src/production-document.ts +2 -0
- package/src/production-native-codecs.ts +1141 -0
- package/src/production-presentation.ts +2 -0
- package/src/production-sha256.ts +150 -0
- package/src/production-spreadsheet-native.ts +686 -0
- package/src/production-spreadsheet.ts +2 -0
- package/src/production.ts +1022 -0
- package/src/raster-image.ts +586 -0
- package/src/reference.ts +17 -0
- package/src/runtime-cli-entry.ts +13 -0
- package/src/runtime-cli.ts +670 -0
- package/src/runtime-development.ts +689 -0
- package/src/runtime-receipt.ts +169 -0
- package/src/runtime.ts +1463 -0
- package/src/snapshot.ts +88 -0
- package/src/spreadsheet-address.ts +90 -0
- package/src/spreadsheet-file.ts +71 -0
- package/src/spreadsheet-formula.ts +1212 -0
- package/src/spreadsheet-image.ts +242 -0
- package/src/spreadsheet-render.ts +985 -0
- package/src/spreadsheet-types.ts +171 -0
- package/src/spreadsheet-xlsx-api.ts +123 -0
- package/src/spreadsheet-xlsx-codec.ts +3063 -0
- package/src/spreadsheet-xlsx-state.ts +191 -0
- package/src/spreadsheet.ts +5195 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
const FONT_FILES_ENV = "OPENGENI_ARTIFACT_RASTER_FONT_FILES";
|
|
2
|
+
const DEFAULT_FONT_FAMILY_ENV = "OPENGENI_ARTIFACT_RASTER_DEFAULT_FONT_FAMILY";
|
|
3
|
+
const MAX_FONT_FILES = 16;
|
|
4
|
+
const MAX_FONT_FILE_BYTES = 32 * 1024 * 1024;
|
|
5
|
+
|
|
6
|
+
type ResvgOptions = {
|
|
7
|
+
background?: string;
|
|
8
|
+
fitTo?: { mode: "zoom"; value: number };
|
|
9
|
+
font: {
|
|
10
|
+
defaultFontFamily?: string;
|
|
11
|
+
fontFiles?: string[];
|
|
12
|
+
loadSystemFonts: boolean;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type ResvgRuntime = {
|
|
17
|
+
Resvg: new (svg: string, options: ResvgOptions) => { render(): { asPng(): Uint8Array } };
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type FontAuthority = ResvgOptions["font"];
|
|
21
|
+
|
|
22
|
+
let cachedFontAuthority: { key: string; value: Promise<FontAuthority> } | undefined;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Rasterize with an explicit font authority when the host configures one.
|
|
26
|
+
* Resvg otherwise succeeds while silently dropping every glyph on minimal
|
|
27
|
+
* Linux images. Sandboxes pin regular/bold/italic-capable Liberation faces;
|
|
28
|
+
* desktop installs retain their native system-font fallback.
|
|
29
|
+
*/
|
|
30
|
+
export async function rasterizeSvgToPng(
|
|
31
|
+
svg: string,
|
|
32
|
+
options: Readonly<{ background?: string; zoom?: number }> = {},
|
|
33
|
+
): Promise<Uint8Array> {
|
|
34
|
+
const [{ Resvg }, font] = await Promise.all([
|
|
35
|
+
importNativeRuntime<ResvgRuntime>("@resvg/resvg-js"),
|
|
36
|
+
rasterFontAuthority(),
|
|
37
|
+
]);
|
|
38
|
+
const resvgOptions: ResvgOptions = {
|
|
39
|
+
...(options.background === undefined ? {} : { background: options.background }),
|
|
40
|
+
...(options.zoom === undefined
|
|
41
|
+
? {}
|
|
42
|
+
: { fitTo: { mode: "zoom" as const, value: options.zoom } }),
|
|
43
|
+
font,
|
|
44
|
+
};
|
|
45
|
+
return Uint8Array.from(new Resvg(svg, resvgOptions).render().asPng());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function importNativeRuntime<T>(specifier: string): Promise<T> {
|
|
49
|
+
return (await import(/* @vite-ignore */ specifier)) as T;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function rasterFontAuthority(): Promise<FontAuthority> {
|
|
53
|
+
const environment = typeof process === "undefined" ? undefined : process.env;
|
|
54
|
+
const filesJson = environment?.[FONT_FILES_ENV];
|
|
55
|
+
const defaultFontFamily = environment?.[DEFAULT_FONT_FAMILY_ENV];
|
|
56
|
+
if (filesJson === undefined && defaultFontFamily === undefined) {
|
|
57
|
+
return { loadSystemFonts: true };
|
|
58
|
+
}
|
|
59
|
+
if (
|
|
60
|
+
!filesJson ||
|
|
61
|
+
filesJson.length > 8_192 ||
|
|
62
|
+
!defaultFontFamily?.trim() ||
|
|
63
|
+
defaultFontFamily.length > 128 ||
|
|
64
|
+
/[\u0000-\u001f\u007f]/u.test(defaultFontFamily)
|
|
65
|
+
) {
|
|
66
|
+
throw new Error(`${FONT_FILES_ENV} and ${DEFAULT_FONT_FAMILY_ENV} must be configured together`);
|
|
67
|
+
}
|
|
68
|
+
const key = `${filesJson}\u0000${defaultFontFamily}`;
|
|
69
|
+
if (cachedFontAuthority?.key === key) return await cachedFontAuthority.value;
|
|
70
|
+
const value = validateConfiguredFontAuthority(filesJson, defaultFontFamily.trim());
|
|
71
|
+
cachedFontAuthority = { key, value };
|
|
72
|
+
return await value;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function validateConfiguredFontAuthority(
|
|
76
|
+
filesJson: string,
|
|
77
|
+
defaultFontFamily: string,
|
|
78
|
+
): Promise<FontAuthority> {
|
|
79
|
+
let requested: unknown;
|
|
80
|
+
try {
|
|
81
|
+
requested = JSON.parse(filesJson);
|
|
82
|
+
} catch {
|
|
83
|
+
throw new Error(`${FONT_FILES_ENV} must be a JSON array of absolute font file paths`);
|
|
84
|
+
}
|
|
85
|
+
if (
|
|
86
|
+
!Array.isArray(requested) ||
|
|
87
|
+
requested.length === 0 ||
|
|
88
|
+
requested.length > MAX_FONT_FILES ||
|
|
89
|
+
requested.some((path) => typeof path !== "string" || path.length === 0)
|
|
90
|
+
) {
|
|
91
|
+
throw new Error(`${FONT_FILES_ENV} must contain 1-${MAX_FONT_FILES} font file paths`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Keep browser HTML/SVG render paths free of Node builtins. This function is
|
|
95
|
+
// reached only by the native PNG/WebP branches with an explicit authority.
|
|
96
|
+
const fileSystemSpecifier = "node:fs/promises";
|
|
97
|
+
const pathSpecifier = "node:path";
|
|
98
|
+
const [{ open, realpath, stat }, { isAbsolute }] = await Promise.all([
|
|
99
|
+
import(/* @vite-ignore */ fileSystemSpecifier) as Promise<typeof import("node:fs/promises")>,
|
|
100
|
+
import(/* @vite-ignore */ pathSpecifier) as Promise<typeof import("node:path")>,
|
|
101
|
+
]);
|
|
102
|
+
const fontFiles = await Promise.all(
|
|
103
|
+
requested.map(async (path) => {
|
|
104
|
+
if (!isAbsolute(path)) throw new Error(`${FONT_FILES_ENV} paths must be absolute`);
|
|
105
|
+
const canonical = await realpath(path);
|
|
106
|
+
const metadata = await stat(canonical);
|
|
107
|
+
if (!metadata.isFile() || metadata.size === 0 || metadata.size > MAX_FONT_FILE_BYTES) {
|
|
108
|
+
throw new Error(`Configured artifact raster font is not a bounded regular file: ${path}`);
|
|
109
|
+
}
|
|
110
|
+
const handle = await open(canonical, "r");
|
|
111
|
+
try {
|
|
112
|
+
const signature = new Uint8Array(4);
|
|
113
|
+
const { bytesRead } = await handle.read(signature, 0, signature.byteLength, 0);
|
|
114
|
+
const tag = new TextDecoder().decode(signature);
|
|
115
|
+
if (
|
|
116
|
+
bytesRead !== 4 ||
|
|
117
|
+
(tag !== "OTTO" && tag !== "ttcf" && !sameBytes(signature, [0, 1, 0, 0]))
|
|
118
|
+
) {
|
|
119
|
+
throw new Error(`Configured artifact raster font has an unsupported signature: ${path}`);
|
|
120
|
+
}
|
|
121
|
+
} finally {
|
|
122
|
+
await handle.close();
|
|
123
|
+
}
|
|
124
|
+
return canonical;
|
|
125
|
+
}),
|
|
126
|
+
);
|
|
127
|
+
if (new Set(fontFiles).size !== fontFiles.length) {
|
|
128
|
+
throw new Error(`${FONT_FILES_ENV} must not contain duplicate files`);
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
defaultFontFamily,
|
|
132
|
+
fontFiles,
|
|
133
|
+
loadSystemFonts: false,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function sameBytes(actual: Uint8Array, expected: readonly number[]): boolean {
|
|
138
|
+
return (
|
|
139
|
+
actual.length === expected.length && actual.every((value, index) => value === expected[index])
|
|
140
|
+
);
|
|
141
|
+
}
|
package/src/native.ts
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EDITABLE_ARTIFACT_INTENT_VERSION,
|
|
3
|
+
SPREADSHEET_ARTIFACT_COMMAND_VERSION,
|
|
4
|
+
assertCanonicalSpreadsheetArtifactCommandBytes,
|
|
5
|
+
decodeEditableArtifactMutationIntent,
|
|
6
|
+
encodeEditableArtifactMutationIntent,
|
|
7
|
+
encodeSpreadsheetArtifactCommandBatch,
|
|
8
|
+
hashEditableArtifactMutationIntentBytes,
|
|
9
|
+
type EditableArtifactMutationIntent,
|
|
10
|
+
type SpreadsheetArtifactCommandBatch,
|
|
11
|
+
} from "@opengeni/contracts/editable-artifacts";
|
|
12
|
+
import {
|
|
13
|
+
ArtifactCollaborationSession,
|
|
14
|
+
ArtifactDocumentSession,
|
|
15
|
+
ArtifactKernelRuntime,
|
|
16
|
+
ArtifactPresentationSession,
|
|
17
|
+
ArtifactRuntimeError,
|
|
18
|
+
ArtifactTextLayoutSession,
|
|
19
|
+
type ArtifactKernelCapabilities,
|
|
20
|
+
type NativeArtifactRuntimeTarget,
|
|
21
|
+
} from "./runtime";
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
SPREADSHEET_ARTIFACT_COMMAND_VERSION,
|
|
25
|
+
assertCanonicalSpreadsheetArtifactCommandBytes,
|
|
26
|
+
decodeSpreadsheetArtifactCommandBatch,
|
|
27
|
+
editableArtifactStableId,
|
|
28
|
+
encodeSpreadsheetArtifactCommandBatch,
|
|
29
|
+
spreadsheetSheetId,
|
|
30
|
+
} from "@opengeni/contracts/editable-artifacts";
|
|
31
|
+
export type {
|
|
32
|
+
EditableArtifactStableId,
|
|
33
|
+
SpreadsheetArtifactCommand,
|
|
34
|
+
SpreadsheetArtifactCommandBatch,
|
|
35
|
+
SpreadsheetCellInput,
|
|
36
|
+
SpreadsheetCellPoint,
|
|
37
|
+
SpreadsheetCellRange,
|
|
38
|
+
SpreadsheetSheetGeneration,
|
|
39
|
+
SpreadsheetSheetPrecondition,
|
|
40
|
+
} from "@opengeni/contracts/editable-artifacts";
|
|
41
|
+
|
|
42
|
+
export type NativeArtifactModality = "spreadsheet" | "document" | "presentation";
|
|
43
|
+
|
|
44
|
+
export type NativeSpreadsheetIntentFields = Omit<
|
|
45
|
+
EditableArtifactMutationIntent,
|
|
46
|
+
| "envelopeVersion"
|
|
47
|
+
| "protocolVersion"
|
|
48
|
+
| "modelSchemaVersion"
|
|
49
|
+
| "commandProtocolVersion"
|
|
50
|
+
| "commandBytes"
|
|
51
|
+
>;
|
|
52
|
+
|
|
53
|
+
export type NativeSpreadsheetAuthorCommandsInput = Readonly<{
|
|
54
|
+
intent: NativeSpreadsheetIntentFields;
|
|
55
|
+
commands: SpreadsheetArtifactCommandBatch;
|
|
56
|
+
/** Exact authoritative OGACF001 base selected for this apply attempt. */
|
|
57
|
+
resolvedBaseBytes: Uint8Array;
|
|
58
|
+
}>;
|
|
59
|
+
|
|
60
|
+
export type NativeSpreadsheetAuthoredTransaction = Readonly<{
|
|
61
|
+
/** Exact canonical OGATX001 bytes passed to Rust. */
|
|
62
|
+
intentBytes: Uint8Array;
|
|
63
|
+
requestHash: string;
|
|
64
|
+
/** One whole canonical OGACO001 transaction returned by Rust. */
|
|
65
|
+
committedTransactionBytes: Uint8Array;
|
|
66
|
+
}>;
|
|
67
|
+
|
|
68
|
+
export type CreateNativeArtifactSessionInput = Readonly<{
|
|
69
|
+
modality: NativeArtifactModality;
|
|
70
|
+
replicaNamespace: bigint;
|
|
71
|
+
}>;
|
|
72
|
+
|
|
73
|
+
export type OpenNativeArtifactSessionInput = Readonly<{
|
|
74
|
+
modality: NativeArtifactModality;
|
|
75
|
+
snapshot: Uint8Array;
|
|
76
|
+
}>;
|
|
77
|
+
|
|
78
|
+
export type NativeArtifactSession =
|
|
79
|
+
| NativeSpreadsheetSession
|
|
80
|
+
| NativeDocumentSession
|
|
81
|
+
| NativePresentationSession;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Synchronous Node/Bun production spreadsheet session backed only by the
|
|
85
|
+
* verified Rust N-API collaboration kernel. It never selects the TypeScript
|
|
86
|
+
* reference model and deliberately rejects a browser/WASM runtime; browsers
|
|
87
|
+
* use the asynchronous `@opengeni/sdk/editable-artifacts` Worker API.
|
|
88
|
+
*/
|
|
89
|
+
export class NativeSpreadsheetSession {
|
|
90
|
+
readonly kind = "native" as const;
|
|
91
|
+
readonly modality = "spreadsheet" as const;
|
|
92
|
+
readonly target: NativeArtifactRuntimeTarget;
|
|
93
|
+
readonly capabilities: ArtifactKernelCapabilities;
|
|
94
|
+
readonly buildIdentity: string;
|
|
95
|
+
|
|
96
|
+
private constructor(
|
|
97
|
+
private readonly runtime: ArtifactKernelRuntime,
|
|
98
|
+
private readonly session: ArtifactCollaborationSession,
|
|
99
|
+
) {
|
|
100
|
+
requireNativeSpreadsheetRuntime(runtime);
|
|
101
|
+
this.target = runtime.target as NativeArtifactRuntimeTarget;
|
|
102
|
+
this.capabilities = runtime.capabilities;
|
|
103
|
+
this.buildIdentity = runtime.buildIdentity;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
static create(
|
|
107
|
+
runtime: ArtifactKernelRuntime,
|
|
108
|
+
replicaNamespace: bigint,
|
|
109
|
+
): NativeSpreadsheetSession {
|
|
110
|
+
requireNativeSpreadsheetRuntime(runtime);
|
|
111
|
+
return new NativeSpreadsheetSession(
|
|
112
|
+
runtime,
|
|
113
|
+
runtime.createCollaborationSession(replicaNamespace),
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
static open(runtime: ArtifactKernelRuntime, snapshot: Uint8Array): NativeSpreadsheetSession {
|
|
118
|
+
requireNativeSpreadsheetRuntime(runtime);
|
|
119
|
+
return new NativeSpreadsheetSession(runtime, runtime.openCollaborationSession(snapshot));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Authors one already-canonical mutation. Rust independently validates the
|
|
124
|
+
* exact OGATX/OGASC bytes, resolves typed operations, applies atomically, and
|
|
125
|
+
* returns the whole canonical OGACO transaction.
|
|
126
|
+
*/
|
|
127
|
+
authorTransaction(intentBytes: Uint8Array, resolvedBaseBytes: Uint8Array): Uint8Array {
|
|
128
|
+
assertSpreadsheetIntent(intentBytes);
|
|
129
|
+
return this.session.authorTransaction(intentBytes, resolvedBaseBytes);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Encodes one identity-free typed command batch and authors it atomically. */
|
|
133
|
+
authorCommands(
|
|
134
|
+
input: NativeSpreadsheetAuthorCommandsInput,
|
|
135
|
+
): NativeSpreadsheetAuthoredTransaction {
|
|
136
|
+
const commandBytes = encodeSpreadsheetArtifactCommandBatch(input.commands);
|
|
137
|
+
const intentBytes = encodeEditableArtifactMutationIntent({
|
|
138
|
+
...input.intent,
|
|
139
|
+
envelopeVersion: EDITABLE_ARTIFACT_INTENT_VERSION,
|
|
140
|
+
protocolVersion: 1,
|
|
141
|
+
modelSchemaVersion: 1,
|
|
142
|
+
commandProtocolVersion: SPREADSHEET_ARTIFACT_COMMAND_VERSION,
|
|
143
|
+
commandBytes,
|
|
144
|
+
});
|
|
145
|
+
const requestHash = hashEditableArtifactMutationIntentBytes(intentBytes);
|
|
146
|
+
const committedTransactionBytes = this.authorTransaction(intentBytes, input.resolvedBaseBytes);
|
|
147
|
+
return Object.freeze({ intentBytes, requestHash, committedTransactionBytes });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Replays one whole authoritative OGACO transaction atomically. */
|
|
151
|
+
applyCommitted(committedTransactionBytes: Uint8Array): void {
|
|
152
|
+
this.session.applyCommitted(committedTransactionBytes);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Executes one canonical OGAKQ request. Typed viewport/metadata helpers use
|
|
157
|
+
* the shared contract codec; this raw method remains useful at server byte
|
|
158
|
+
* boundaries and never exposes a Rust object layout.
|
|
159
|
+
*/
|
|
160
|
+
query(queryBytes: Uint8Array): Uint8Array {
|
|
161
|
+
return this.session.query(queryBytes);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
snapshot(): Uint8Array {
|
|
165
|
+
return this.session.snapshot();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
frontier(): Uint8Array {
|
|
169
|
+
return this.session.frontier();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
stateHash(): string {
|
|
173
|
+
return this.session.stateHash();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
revision(): bigint {
|
|
177
|
+
return this.session.revision();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
fork(): NativeSpreadsheetSession {
|
|
181
|
+
return new NativeSpreadsheetSession(this.runtime, this.session.fork());
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
isClosed(): boolean {
|
|
185
|
+
return this.session.isClosed();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
dispose(): void {
|
|
189
|
+
this.session.dispose();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Exact synchronous N-API structured-document byte session. */
|
|
194
|
+
export class NativeDocumentSession {
|
|
195
|
+
readonly kind = "native" as const;
|
|
196
|
+
readonly modality = "document" as const;
|
|
197
|
+
readonly target: NativeArtifactRuntimeTarget;
|
|
198
|
+
readonly capabilities: ArtifactKernelCapabilities;
|
|
199
|
+
readonly buildIdentity: string;
|
|
200
|
+
|
|
201
|
+
private constructor(
|
|
202
|
+
private readonly runtime: ArtifactKernelRuntime,
|
|
203
|
+
private readonly session: ArtifactDocumentSession,
|
|
204
|
+
) {
|
|
205
|
+
requireNativeRuntime(runtime);
|
|
206
|
+
this.target = runtime.target as NativeArtifactRuntimeTarget;
|
|
207
|
+
this.capabilities = runtime.capabilities;
|
|
208
|
+
this.buildIdentity = runtime.buildIdentity;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
static create(runtime: ArtifactKernelRuntime, namespace: bigint): NativeDocumentSession {
|
|
212
|
+
requireNativeRuntime(runtime);
|
|
213
|
+
return new NativeDocumentSession(runtime, runtime.createDocumentSession(namespace));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
static open(runtime: ArtifactKernelRuntime, snapshot: Uint8Array): NativeDocumentSession {
|
|
217
|
+
requireNativeRuntime(runtime);
|
|
218
|
+
return new NativeDocumentSession(runtime, runtime.openDocumentSession(snapshot));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
applyCommands(commands: Uint8Array): Uint8Array {
|
|
222
|
+
return this.session.applyCommands(commands);
|
|
223
|
+
}
|
|
224
|
+
query(request: Uint8Array): Uint8Array {
|
|
225
|
+
return this.session.query(request);
|
|
226
|
+
}
|
|
227
|
+
snapshot(): Uint8Array {
|
|
228
|
+
return this.session.snapshot();
|
|
229
|
+
}
|
|
230
|
+
revision(): bigint {
|
|
231
|
+
return this.session.revision();
|
|
232
|
+
}
|
|
233
|
+
stateHash(): string {
|
|
234
|
+
return this.session.stateHash();
|
|
235
|
+
}
|
|
236
|
+
fork(): NativeDocumentSession {
|
|
237
|
+
return new NativeDocumentSession(this.runtime, this.session.fork());
|
|
238
|
+
}
|
|
239
|
+
isClosed(): boolean {
|
|
240
|
+
return this.session.isClosed();
|
|
241
|
+
}
|
|
242
|
+
dispose(): void {
|
|
243
|
+
this.session.dispose();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** Exact synchronous N-API presentation byte session. */
|
|
248
|
+
export class NativePresentationSession {
|
|
249
|
+
readonly kind = "native" as const;
|
|
250
|
+
readonly modality = "presentation" as const;
|
|
251
|
+
readonly target: NativeArtifactRuntimeTarget;
|
|
252
|
+
readonly capabilities: ArtifactKernelCapabilities;
|
|
253
|
+
readonly buildIdentity: string;
|
|
254
|
+
|
|
255
|
+
private constructor(
|
|
256
|
+
private readonly runtime: ArtifactKernelRuntime,
|
|
257
|
+
private readonly session: ArtifactPresentationSession,
|
|
258
|
+
) {
|
|
259
|
+
requireNativeRuntime(runtime);
|
|
260
|
+
this.target = runtime.target as NativeArtifactRuntimeTarget;
|
|
261
|
+
this.capabilities = runtime.capabilities;
|
|
262
|
+
this.buildIdentity = runtime.buildIdentity;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
static create(runtime: ArtifactKernelRuntime, namespace: bigint): NativePresentationSession {
|
|
266
|
+
requireNativeRuntime(runtime);
|
|
267
|
+
return new NativePresentationSession(runtime, runtime.createPresentationSession(namespace));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
static open(runtime: ArtifactKernelRuntime, snapshot: Uint8Array): NativePresentationSession {
|
|
271
|
+
requireNativeRuntime(runtime);
|
|
272
|
+
return new NativePresentationSession(runtime, runtime.openPresentationSession(snapshot));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
applyCommands(commands: Uint8Array): Uint8Array {
|
|
276
|
+
return this.session.applyCommands(commands);
|
|
277
|
+
}
|
|
278
|
+
query(request: Uint8Array): Uint8Array {
|
|
279
|
+
return this.session.query(request);
|
|
280
|
+
}
|
|
281
|
+
snapshot(): Uint8Array {
|
|
282
|
+
return this.session.snapshot();
|
|
283
|
+
}
|
|
284
|
+
revision(): bigint {
|
|
285
|
+
return this.session.revision();
|
|
286
|
+
}
|
|
287
|
+
stateHash(): string {
|
|
288
|
+
return this.session.stateHash();
|
|
289
|
+
}
|
|
290
|
+
fork(): NativePresentationSession {
|
|
291
|
+
return new NativePresentationSession(this.runtime, this.session.fork());
|
|
292
|
+
}
|
|
293
|
+
isClosed(): boolean {
|
|
294
|
+
return this.session.isClosed();
|
|
295
|
+
}
|
|
296
|
+
dispose(): void {
|
|
297
|
+
this.session.dispose();
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** Exact synchronous N-API retained-font text-layout session. */
|
|
302
|
+
export class NativeTextLayoutSession {
|
|
303
|
+
readonly kind = "native" as const;
|
|
304
|
+
readonly modality = "text-layout" as const;
|
|
305
|
+
readonly target: NativeArtifactRuntimeTarget;
|
|
306
|
+
readonly capabilities: ArtifactKernelCapabilities;
|
|
307
|
+
readonly buildIdentity: string;
|
|
308
|
+
|
|
309
|
+
private constructor(
|
|
310
|
+
private readonly session: ArtifactTextLayoutSession,
|
|
311
|
+
runtime: ArtifactKernelRuntime,
|
|
312
|
+
) {
|
|
313
|
+
requireNativeRuntime(runtime);
|
|
314
|
+
this.target = runtime.target as NativeArtifactRuntimeTarget;
|
|
315
|
+
this.capabilities = runtime.capabilities;
|
|
316
|
+
this.buildIdentity = runtime.buildIdentity;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
static open(runtime: ArtifactKernelRuntime, fontBundle: Uint8Array): NativeTextLayoutSession {
|
|
320
|
+
requireNativeRuntime(runtime);
|
|
321
|
+
return new NativeTextLayoutSession(runtime.openTextLayoutSession(fontBundle), runtime);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
layout(request: Uint8Array): Uint8Array {
|
|
325
|
+
return this.session.layout(request);
|
|
326
|
+
}
|
|
327
|
+
isClosed(): boolean {
|
|
328
|
+
return this.session.isClosed();
|
|
329
|
+
}
|
|
330
|
+
dispose(): void {
|
|
331
|
+
this.session.dispose();
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** Creates a production modality session; unsupported Rust modes fail closed. */
|
|
336
|
+
export function createNativeArtifactSession(
|
|
337
|
+
runtime: ArtifactKernelRuntime,
|
|
338
|
+
input: CreateNativeArtifactSessionInput,
|
|
339
|
+
): NativeArtifactSession {
|
|
340
|
+
if (input.modality === "spreadsheet") {
|
|
341
|
+
return NativeSpreadsheetSession.create(runtime, input.replicaNamespace);
|
|
342
|
+
}
|
|
343
|
+
if (input.modality === "document") {
|
|
344
|
+
return NativeDocumentSession.create(runtime, input.replicaNamespace);
|
|
345
|
+
}
|
|
346
|
+
return NativePresentationSession.create(runtime, input.replicaNamespace);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** Opens a production modality session; unsupported Rust modes fail closed. */
|
|
350
|
+
export function openNativeArtifactSession(
|
|
351
|
+
runtime: ArtifactKernelRuntime,
|
|
352
|
+
input: OpenNativeArtifactSessionInput,
|
|
353
|
+
): NativeArtifactSession {
|
|
354
|
+
if (input.modality === "spreadsheet")
|
|
355
|
+
return NativeSpreadsheetSession.open(runtime, input.snapshot);
|
|
356
|
+
if (input.modality === "document") return NativeDocumentSession.open(runtime, input.snapshot);
|
|
357
|
+
return NativePresentationSession.open(runtime, input.snapshot);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function assertSpreadsheetIntent(intentBytes: Uint8Array): void {
|
|
361
|
+
const intent = decodeEditableArtifactMutationIntent(intentBytes);
|
|
362
|
+
if (
|
|
363
|
+
intent.envelopeVersion !== EDITABLE_ARTIFACT_INTENT_VERSION ||
|
|
364
|
+
intent.protocolVersion !== 1 ||
|
|
365
|
+
intent.modelSchemaVersion !== 1 ||
|
|
366
|
+
intent.commandProtocolVersion !== SPREADSHEET_ARTIFACT_COMMAND_VERSION
|
|
367
|
+
) {
|
|
368
|
+
throw new ArtifactRuntimeError(
|
|
369
|
+
"ARTIFACT_RUNTIME_INCOMPATIBLE",
|
|
370
|
+
"Spreadsheet intent versions do not match the loaded production kernel",
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
assertCanonicalSpreadsheetArtifactCommandBytes(intent.commandBytes);
|
|
374
|
+
// Hashing decodes again by design and proves the exact outer bytes are an
|
|
375
|
+
// acceptable canonical request before they cross the native boundary.
|
|
376
|
+
hashEditableArtifactMutationIntentBytes(intentBytes);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function requireNativeSpreadsheetRuntime(runtime: ArtifactKernelRuntime): void {
|
|
380
|
+
requireNativeRuntime(runtime);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function requireNativeRuntime(runtime: ArtifactKernelRuntime): void {
|
|
384
|
+
if (runtime.kind !== "native" || runtime.target === "wasm-web") {
|
|
385
|
+
throw new ArtifactRuntimeError(
|
|
386
|
+
"ARTIFACT_RUNTIME_UNAVAILABLE",
|
|
387
|
+
"NativeSpreadsheetSession requires an exact local N-API kernel; browser editing uses @opengeni/sdk/editable-artifacts",
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
}
|