@seriousgaming/sdk 0.0.0-stage → 1.0.0-alpha.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 (170) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +1 -0
  3. package/README.md +33 -2
  4. package/lib/build/callbacks.d.ts +998 -0
  5. package/lib/build/callbacks.js +32 -0
  6. package/lib/build/context-merge.d.ts +59 -0
  7. package/lib/build/context-merge.js +118 -0
  8. package/lib/build/document.d.ts +36 -0
  9. package/lib/build/document.js +8 -0
  10. package/lib/build/encoding.d.ts +26 -0
  11. package/lib/build/encoding.js +78 -0
  12. package/lib/build/endpoints.d.ts +421 -0
  13. package/lib/build/endpoints.js +46 -0
  14. package/lib/build/hooks.d.ts +74 -0
  15. package/lib/build/hooks.js +1 -0
  16. package/lib/build/index.d.ts +18 -0
  17. package/lib/build/index.js +16 -0
  18. package/lib/build/llm.d.ts +166 -0
  19. package/lib/build/llm.js +17 -0
  20. package/lib/build/resource-schema.d.ts +1048 -0
  21. package/lib/build/resource-schema.js +7 -0
  22. package/lib/build/scheduling.d.ts +112 -0
  23. package/lib/build/scheduling.js +217 -0
  24. package/lib/build/seed.d.ts +51 -0
  25. package/lib/build/seed.js +89 -0
  26. package/lib/build/signatures.d.ts +372 -0
  27. package/lib/build/signatures.js +1 -0
  28. package/lib/build/speech.d.ts +102 -0
  29. package/lib/build/speech.js +11 -0
  30. package/lib/build/tick-validation.d.ts +23 -0
  31. package/lib/build/tick-validation.js +96 -0
  32. package/lib/build/timer.d.ts +74 -0
  33. package/lib/build/timer.js +1 -0
  34. package/lib/build/validators.d.ts +116 -0
  35. package/lib/build/validators.js +1 -0
  36. package/lib/commands.d.ts +357 -0
  37. package/lib/commands.js +14 -0
  38. package/lib/e2e.d.ts +238 -0
  39. package/lib/e2e.js +248 -0
  40. package/lib/projection.d.ts +266 -0
  41. package/lib/projection.js +23 -0
  42. package/lib/testing/index.d.ts +13 -0
  43. package/lib/testing/index.js +13 -0
  44. package/lib/testing/logic-args.d.ts +177 -0
  45. package/lib/testing/logic-args.js +229 -0
  46. package/lib/testing/stub-services.d.ts +41 -0
  47. package/lib/testing/stub-services.js +176 -0
  48. package/lib/ui/CrossTeamDataContext.d.ts +36 -0
  49. package/lib/ui/CrossTeamDataContext.js +32 -0
  50. package/lib/ui/PresentationContext.d.ts +72 -0
  51. package/lib/ui/PresentationContext.js +13 -0
  52. package/lib/ui/PresentationWrapper.d.ts +12 -0
  53. package/lib/ui/PresentationWrapper.js +78 -0
  54. package/lib/ui/SessionUserInterfaceCallbacksContext.d.ts +3 -0
  55. package/lib/ui/SessionUserInterfaceCallbacksContext.js +7 -0
  56. package/lib/ui/SessionUserInterfaceDataContext.d.ts +103 -0
  57. package/lib/ui/SessionUserInterfaceDataContext.js +17 -0
  58. package/lib/ui/SessionUserInterfaceHookFailuresContext.d.ts +44 -0
  59. package/lib/ui/SessionUserInterfaceHookFailuresContext.js +15 -0
  60. package/lib/ui/SessionUserInterfaceHostOverlaysContext.d.ts +23 -0
  61. package/lib/ui/SessionUserInterfaceHostOverlaysContext.js +23 -0
  62. package/lib/ui/SessionUserInterfaceLocaleContext.d.ts +11 -0
  63. package/lib/ui/SessionUserInterfaceLocaleContext.js +13 -0
  64. package/lib/ui/SessionUserInterfaceMetaContext.d.ts +39 -0
  65. package/lib/ui/SessionUserInterfaceMetaContext.js +13 -0
  66. package/lib/ui/SessionUserInterfaceParticipantsContext.d.ts +12 -0
  67. package/lib/ui/SessionUserInterfaceParticipantsContext.js +16 -0
  68. package/lib/ui/SessionUserInterfaceTeamConfigurationContext.d.ts +53 -0
  69. package/lib/ui/SessionUserInterfaceTeamConfigurationContext.js +23 -0
  70. package/lib/ui/SessionUserInterfaceTeamLinksContext.d.ts +56 -0
  71. package/lib/ui/SessionUserInterfaceTeamLinksContext.js +12 -0
  72. package/lib/ui/SessionUserInterfaceTeamsContext.d.ts +13 -0
  73. package/lib/ui/SessionUserInterfaceTeamsContext.js +13 -0
  74. package/lib/ui/SessionUserInterfaceThemeContext.d.ts +9 -0
  75. package/lib/ui/SessionUserInterfaceThemeContext.js +7 -0
  76. package/lib/ui/SessionUserInterfaceThemePreferenceContext.d.ts +3 -0
  77. package/lib/ui/SessionUserInterfaceThemePreferenceContext.js +7 -0
  78. package/lib/ui/SessionUserInterfaceWalkthroughLinkContext.d.ts +42 -0
  79. package/lib/ui/SessionUserInterfaceWalkthroughLinkContext.js +26 -0
  80. package/lib/ui/SessionUserInterfaceWrapper.d.ts +8 -0
  81. package/lib/ui/SessionUserInterfaceWrapper.js +321 -0
  82. package/lib/ui/SettingsEditorCallbacksContext.d.ts +3 -0
  83. package/lib/ui/SettingsEditorCallbacksContext.js +7 -0
  84. package/lib/ui/SettingsEditorContext.d.ts +33 -0
  85. package/lib/ui/SettingsEditorContext.js +16 -0
  86. package/lib/ui/SettingsEditorLocaleContext.d.ts +13 -0
  87. package/lib/ui/SettingsEditorLocaleContext.js +15 -0
  88. package/lib/ui/SettingsEditorThemeContext.d.ts +9 -0
  89. package/lib/ui/SettingsEditorThemeContext.js +7 -0
  90. package/lib/ui/SettingsEditorThemePreferenceContext.d.ts +3 -0
  91. package/lib/ui/SettingsEditorThemePreferenceContext.js +7 -0
  92. package/lib/ui/SettingsEditorWrapper.d.ts +13 -0
  93. package/lib/ui/SettingsEditorWrapper.js +74 -0
  94. package/lib/ui/UserInterfaceCallbacksContext.d.ts +3 -0
  95. package/lib/ui/UserInterfaceCallbacksContext.js +7 -0
  96. package/lib/ui/UserInterfaceDataContext.d.ts +78 -0
  97. package/lib/ui/UserInterfaceDataContext.js +11 -0
  98. package/lib/ui/UserInterfaceLocaleContext.d.ts +13 -0
  99. package/lib/ui/UserInterfaceLocaleContext.js +15 -0
  100. package/lib/ui/UserInterfaceOwnTeamContext.d.ts +25 -0
  101. package/lib/ui/UserInterfaceOwnTeamContext.js +26 -0
  102. package/lib/ui/UserInterfaceOwnTeamJoinLinkContext.d.ts +52 -0
  103. package/lib/ui/UserInterfaceOwnTeamJoinLinkContext.js +13 -0
  104. package/lib/ui/UserInterfaceParticipantContext.d.ts +16 -0
  105. package/lib/ui/UserInterfaceParticipantContext.js +24 -0
  106. package/lib/ui/UserInterfaceParticipantsContext.d.ts +12 -0
  107. package/lib/ui/UserInterfaceParticipantsContext.js +13 -0
  108. package/lib/ui/UserInterfaceRunKindContext.d.ts +31 -0
  109. package/lib/ui/UserInterfaceRunKindContext.js +31 -0
  110. package/lib/ui/UserInterfaceTeamsContext.d.ts +12 -0
  111. package/lib/ui/UserInterfaceTeamsContext.js +13 -0
  112. package/lib/ui/UserInterfaceThemeContext.d.ts +9 -0
  113. package/lib/ui/UserInterfaceThemeContext.js +7 -0
  114. package/lib/ui/UserInterfaceThemePreferenceContext.d.ts +3 -0
  115. package/lib/ui/UserInterfaceThemePreferenceContext.js +7 -0
  116. package/lib/ui/UserInterfaceTimerContext.d.ts +10 -0
  117. package/lib/ui/UserInterfaceTimerContext.js +12 -0
  118. package/lib/ui/UserInterfaceValidationContext.d.ts +25 -0
  119. package/lib/ui/UserInterfaceValidationContext.js +14 -0
  120. package/lib/ui/UserInterfaceViewerRoleContext.d.ts +14 -0
  121. package/lib/ui/UserInterfaceViewerRoleContext.js +17 -0
  122. package/lib/ui/UserInterfaceWrapper.d.ts +8 -0
  123. package/lib/ui/UserInterfaceWrapper.js +219 -0
  124. package/lib/ui/expectedParentOrigin.d.ts +10 -0
  125. package/lib/ui/expectedParentOrigin.js +32 -0
  126. package/lib/ui/index.d.ts +50 -0
  127. package/lib/ui/index.js +52 -0
  128. package/lib/ui/internal/SurfaceGate.d.ts +19 -0
  129. package/lib/ui/internal/SurfaceGate.js +17 -0
  130. package/lib/ui/internal/SurfaceScope.d.ts +17 -0
  131. package/lib/ui/internal/SurfaceScope.js +16 -0
  132. package/lib/ui/internal/createContexts.d.ts +50 -0
  133. package/lib/ui/internal/createContexts.js +32 -0
  134. package/lib/ui/internal/document-attributes.d.ts +9 -0
  135. package/lib/ui/internal/document-attributes.js +87 -0
  136. package/lib/ui/internal/surface-timer.d.ts +3 -0
  137. package/lib/ui/internal/surface-timer.js +11 -0
  138. package/lib/ui/internal/useLatestRef.d.ts +7 -0
  139. package/lib/ui/internal/useLatestRef.js +11 -0
  140. package/lib/ui/internal/useOptimisticMerge.d.ts +19 -0
  141. package/lib/ui/internal/useOptimisticMerge.js +28 -0
  142. package/lib/ui/internal/useSurfaceControls.d.ts +52 -0
  143. package/lib/ui/internal/useSurfaceControls.js +19 -0
  144. package/lib/ui/internal/useTypedPastState.d.ts +17 -0
  145. package/lib/ui/internal/useTypedPastState.js +17 -0
  146. package/lib/ui/mutation-commit.d.ts +32 -0
  147. package/lib/ui/mutation-commit.js +1 -0
  148. package/lib/ui/mutation-outcome.d.ts +66 -0
  149. package/lib/ui/mutation-outcome.js +49 -0
  150. package/lib/ui/request-id.d.ts +18 -0
  151. package/lib/ui/request-id.js +44 -0
  152. package/lib/ui/testing.d.ts +365 -0
  153. package/lib/ui/testing.js +572 -0
  154. package/lib/ui/tick-snapshot.d.ts +28 -0
  155. package/lib/ui/tick-snapshot.js +34 -0
  156. package/lib/ui/ui-protocol.d.ts +799 -0
  157. package/lib/ui/ui-protocol.js +50 -0
  158. package/lib/ui/useAsset.d.ts +80 -0
  159. package/lib/ui/useAsset.js +139 -0
  160. package/lib/ui/useEndpointCallbacks.d.ts +31 -0
  161. package/lib/ui/useEndpointCallbacks.js +68 -0
  162. package/lib/ui/useHostChannel.d.ts +90 -0
  163. package/lib/ui/useHostChannel.js +338 -0
  164. package/lib/ui/useMutationReporter.d.ts +100 -0
  165. package/lib/ui/useMutationReporter.js +97 -0
  166. package/lib/ui/usePastState.d.ts +39 -0
  167. package/lib/ui/usePastState.js +63 -0
  168. package/lib/ui/useViewerStorage.d.ts +54 -0
  169. package/lib/ui/useViewerStorage.js +141 -0
  170. package/package.json +89 -3
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Callback interfaces for SDK components
3
+ *
4
+ * Defines the callbacks available to the engine, user interfaces, and configuration
5
+ * user interfaces. LLM-specific types are imported from ./llm.
6
+ *
7
+ * Asset references — every callback below that takes or returns a file
8
+ * identifier uses the unified asset URI scheme:
9
+ *
10
+ * `asset:<path>` — package-bundled file. The path is relative to the
11
+ * active scenario's `assets/` directory.
12
+ * `blob:<id>` — runtime-uploaded file. The id is whatever the runtime
13
+ * assigns when the blob was stored.
14
+ *
15
+ * Any string starting with `asset:` or `blob:` is unambiguously an asset
16
+ * reference. No other strings are.
17
+ */
18
+ /**
19
+ * Error thrown when a resource invokes a callback it did not declare in its
20
+ * bundle's `uses` array. Carries the offending resource identifier and the
21
+ * callback name so a runtime can surface both to the operator.
22
+ */
23
+ export class UndeclaredApiUseError extends Error {
24
+ resourceIdentifier;
25
+ api;
26
+ constructor(resourceIdentifier, api) {
27
+ super(`Resource "${resourceIdentifier}" attempted to call callback "${api}" but did not declare it in its bundle's "uses" array. Add "${api}" to the bundle's uses to allow this call.`);
28
+ this.name = 'UndeclaredApiUseError';
29
+ this.resourceIdentifier = resourceIdentifier;
30
+ this.api = api;
31
+ }
32
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * The §9.5 context deep-merge, as the UI wrappers apply it to their optimistic
3
+ * local copy. Internal to the SDK: no entry point exports the functions here,
4
+ * only the `ContextUpdate` and `ContextDeletePath` types.
5
+ *
6
+ * The runtime applies the same merge from its own copy in
7
+ * `@seriousgaming/sdk-tools/runtime`, and
8
+ * `context-merge.parity.test.ts` there holds the two to one table of cases.
9
+ * Both must apply identical semantics or a UI sending a minimal patch sees its
10
+ * local view diverge from the host's until the next snapshot:
11
+ *
12
+ * - Plain objects are merged recursively
13
+ * - Arrays, scalars, `null`, and non-plain objects replace the base value
14
+ * - `__proto__`, `constructor`, and `prototype` keys are ignored (payloads
15
+ * cross an untrusted iframe boundary)
16
+ *
17
+ * A deep merge cannot express key deletion, so an update carries deletions
18
+ * beside the patch rather than inside it (see {@link ContextDeletePath} and
19
+ * {@link applyContextUpdate}).
20
+ */
21
+ /**
22
+ * A partial update accepted by `setData` and merged with `deepMergeContext`.
23
+ * Mirrors the merge semantics at the type level: object values may be given
24
+ * partially (they merge), while arrays and scalars must be given whole (they
25
+ * replace).
26
+ */
27
+ export type ContextUpdate<T> = {
28
+ [K in keyof T]?: T[K] extends ReadonlyArray<unknown> ? T[K] : T[K] extends Record<string, unknown> ? ContextUpdate<T[K]> : T[K];
29
+ };
30
+ /**
31
+ * Where to remove a key, as a path from the root of the context.
32
+ *
33
+ * The dotted form reads best and covers almost everything, because the first
34
+ * two segments are a capability identifier and a variable name. Give the
35
+ * segments as an array where a key of your own could contain a dot — a
36
+ * collection keyed by something arbitrary, say — and nothing has to be
37
+ * escaped.
38
+ *
39
+ * ```typescript
40
+ * 'pinboard.Notes.01H8XY'
41
+ * ['pinboard', 'Notes', 'a.key.with.dots']
42
+ * ```
43
+ */
44
+ export type ContextDeletePath = string | readonly string[];
45
+ export declare function deepMergeContext<T extends Record<string, unknown>>(base: T, patch: Record<string, unknown>): T;
46
+ /**
47
+ * The parts of `update` that differ from `current`, or undefined when nothing
48
+ * does. Plain objects are compared key by key; anything else by value.
49
+ */
50
+ export declare function changedFrom(update: unknown, current: unknown): unknown;
51
+ /**
52
+ * Apply one context update: merge the patch, then remove the deleted paths.
53
+ *
54
+ * Deletions are applied after the merge so a single update can replace an
55
+ * entry — write the new key, drop the old — without the order of the two
56
+ * mattering to the caller. Every runtime applying an update MUST use this
57
+ * order (SPECIFICATION.md §9.5).
58
+ */
59
+ export declare function applyContextUpdate<T extends Record<string, unknown>>(base: T, patch: Record<string, unknown>, deletes?: readonly ContextDeletePath[]): T;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * The §9.5 context deep-merge, as the UI wrappers apply it to their optimistic
3
+ * local copy. Internal to the SDK: no entry point exports the functions here,
4
+ * only the `ContextUpdate` and `ContextDeletePath` types.
5
+ *
6
+ * The runtime applies the same merge from its own copy in
7
+ * `@seriousgaming/sdk-tools/runtime`, and
8
+ * `context-merge.parity.test.ts` there holds the two to one table of cases.
9
+ * Both must apply identical semantics or a UI sending a minimal patch sees its
10
+ * local view diverge from the host's until the next snapshot:
11
+ *
12
+ * - Plain objects are merged recursively
13
+ * - Arrays, scalars, `null`, and non-plain objects replace the base value
14
+ * - `__proto__`, `constructor`, and `prototype` keys are ignored (payloads
15
+ * cross an untrusted iframe boundary)
16
+ *
17
+ * A deep merge cannot express key deletion, so an update carries deletions
18
+ * beside the patch rather than inside it (see {@link ContextDeletePath} and
19
+ * {@link applyContextUpdate}).
20
+ */
21
+ const UNSAFE_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
22
+ /**
23
+ * Realm-safe plain-object test.
24
+ *
25
+ * Logic is evaluated in its own V8 context, so the objects it returns
26
+ * carry that realm's `Object.prototype`, not this one's. Comparing against the
27
+ * host's `Object.prototype` alone classifies them as non-plain and replaces
28
+ * them, blanking sibling fields a partial patch never touched (#394). A plain
29
+ * object from any realm sits directly on a prototype that is itself rootless,
30
+ * which `Date`, `Map`, and class instances do not.
31
+ */
32
+ function isPlainObject(value) {
33
+ if (value === null || typeof value !== 'object')
34
+ return false;
35
+ if (Array.isArray(value))
36
+ return false;
37
+ const proto = Object.getPrototypeOf(value);
38
+ if (proto === null || proto === Object.prototype)
39
+ return true;
40
+ return Object.getPrototypeOf(proto) === null;
41
+ }
42
+ export function deepMergeContext(base, patch) {
43
+ const result = { ...base };
44
+ for (const key of Object.keys(patch)) {
45
+ if (UNSAFE_KEYS.has(key))
46
+ continue;
47
+ const baseValue = result[key];
48
+ const patchValue = patch[key];
49
+ result[key] =
50
+ isPlainObject(baseValue) && isPlainObject(patchValue)
51
+ ? deepMergeContext(baseValue, patchValue)
52
+ : patchValue;
53
+ }
54
+ return result;
55
+ }
56
+ /**
57
+ * The parts of `update` that differ from `current`, or undefined when nothing
58
+ * does. Plain objects are compared key by key; anything else by value.
59
+ */
60
+ export function changedFrom(update, current) {
61
+ if (isPlainObject(update) && isPlainObject(current)) {
62
+ const changed = {};
63
+ for (const [key, value] of Object.entries(update)) {
64
+ if (UNSAFE_KEYS.has(key))
65
+ continue;
66
+ const difference = changedFrom(value, current[key]);
67
+ if (difference !== undefined)
68
+ changed[key] = difference;
69
+ }
70
+ return Object.keys(changed).length > 0 ? changed : undefined;
71
+ }
72
+ return JSON.stringify(update) === JSON.stringify(current) ? undefined : update;
73
+ }
74
+ function segmentsOf(path) {
75
+ return typeof path === 'string' ? path.split('.') : [...path];
76
+ }
77
+ /**
78
+ * Remove one path from a context, cloning only the objects along the way.
79
+ *
80
+ * A path that does not resolve is not an error: two participants clearing the
81
+ * same note both send the delete, and the second arriving to find it already
82
+ * gone is the system working.
83
+ */
84
+ function deletePath(base, path) {
85
+ const segments = segmentsOf(path);
86
+ if (segments.length === 0 || segments.some((segment) => UNSAFE_KEYS.has(segment))) {
87
+ return base;
88
+ }
89
+ const [head, ...rest] = segments;
90
+ if (!Object.prototype.hasOwnProperty.call(base, head))
91
+ return base;
92
+ if (rest.length === 0) {
93
+ const result = { ...base };
94
+ delete result[head];
95
+ return result;
96
+ }
97
+ const child = base[head];
98
+ if (!isPlainObject(child))
99
+ return base;
100
+ const prunedChild = deletePath(child, rest);
101
+ if (prunedChild === child)
102
+ return base;
103
+ return { ...base, [head]: prunedChild };
104
+ }
105
+ /**
106
+ * Apply one context update: merge the patch, then remove the deleted paths.
107
+ *
108
+ * Deletions are applied after the merge so a single update can replace an
109
+ * entry — write the new key, drop the old — without the order of the two
110
+ * mattering to the caller. Every runtime applying an update MUST use this
111
+ * order (SPECIFICATION.md §9.5).
112
+ */
113
+ export function applyContextUpdate(base, patch, deletes) {
114
+ const merged = deepMergeContext(base, patch);
115
+ if (!deletes || deletes.length === 0)
116
+ return merged;
117
+ return deletes.reduce((context, path) => deletePath(context, path), merged);
118
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Document Extraction Types
3
+ *
4
+ * Provides types for extracting text content from documents (PDF, etc.)
5
+ * through the Serious Gaming SDK. The extracted text is suitable for
6
+ * passing to an LLM for further processing.
7
+ */
8
+ /**
9
+ * Response from extracting text from a document
10
+ */
11
+ export interface ExtractTextResponse {
12
+ /**
13
+ * The extracted text content, typically plain text or markdown
14
+ */
15
+ content: string;
16
+ /**
17
+ * MIME type of the source document (e.g. 'application/pdf')
18
+ */
19
+ mimeType: string;
20
+ }
21
+ /**
22
+ * Function for extracting text from a document referenced under the unified
23
+ * asset URI scheme.
24
+ *
25
+ * The argument is an asset reference: either `asset:<path>` for a package-
26
+ * bundled file resolved against the active scenario's `assets/` directory,
27
+ * or `blob:<id>` for a runtime-uploaded file. The underlying parsing
28
+ * strategy is determined by the platform.
29
+ *
30
+ * Example:
31
+ * ```typescript
32
+ * const fromUpload = await callbacks.extractText("blob:abc123");
33
+ * const fromAssets = await callbacks.extractText("asset:briefing.pdf");
34
+ * ```
35
+ */
36
+ export type ExtractTextFunction = (reference: string) => Promise<ExtractTextResponse>;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Document Extraction Types
3
+ *
4
+ * Provides types for extracting text content from documents (PDF, etc.)
5
+ * through the Serious Gaming SDK. The extracted text is suitable for
6
+ * passing to an LLM for further processing.
7
+ */
8
+ export {};
@@ -0,0 +1,26 @@
1
+ /**
2
+ * UTF-8 encoding helpers for author logic.
3
+ *
4
+ * The production runtime is a bare V8 isolate that exposes only the ECMAScript
5
+ * intrinsics — `TextEncoder`, `TextDecoder`, `Buffer`, `atob`, and `btoa` do
6
+ * not exist there (SPECIFICATION.md §3.2). These helpers implement UTF-8
7
+ * conversion in plain ECMAScript so the engine can produce bytes for `writeBlob`
8
+ * or read bytes from `readAsset` / `extractText` identically in the dev server
9
+ * and in production, with no host global. They are compiled into the author's
10
+ * bundle, so there is nothing for a runtime to provide.
11
+ *
12
+ * Both handle the full Unicode range, including astral-plane code points
13
+ * represented as surrogate pairs.
14
+ */
15
+ /**
16
+ * Encode a string to its UTF-8 byte sequence.
17
+ *
18
+ * A portable replacement for `new TextEncoder().encode(input)`.
19
+ */
20
+ export declare function encodeUtf8(input: string): Uint8Array;
21
+ /**
22
+ * Decode a UTF-8 byte sequence to a string.
23
+ *
24
+ * A portable replacement for `new TextDecoder().decode(bytes)`.
25
+ */
26
+ export declare function decodeUtf8(bytes: Uint8Array): string;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * UTF-8 encoding helpers for author logic.
3
+ *
4
+ * The production runtime is a bare V8 isolate that exposes only the ECMAScript
5
+ * intrinsics — `TextEncoder`, `TextDecoder`, `Buffer`, `atob`, and `btoa` do
6
+ * not exist there (SPECIFICATION.md §3.2). These helpers implement UTF-8
7
+ * conversion in plain ECMAScript so the engine can produce bytes for `writeBlob`
8
+ * or read bytes from `readAsset` / `extractText` identically in the dev server
9
+ * and in production, with no host global. They are compiled into the author's
10
+ * bundle, so there is nothing for a runtime to provide.
11
+ *
12
+ * Both handle the full Unicode range, including astral-plane code points
13
+ * represented as surrogate pairs.
14
+ */
15
+ /**
16
+ * Encode a string to its UTF-8 byte sequence.
17
+ *
18
+ * A portable replacement for `new TextEncoder().encode(input)`.
19
+ */
20
+ export function encodeUtf8(input) {
21
+ const bytes = [];
22
+ // Iterating a string with for…of yields whole code points, so surrogate
23
+ // pairs are already combined.
24
+ for (const character of input) {
25
+ const codePoint = character.codePointAt(0) ?? 0;
26
+ if (codePoint < 0x80) {
27
+ bytes.push(codePoint);
28
+ }
29
+ else if (codePoint < 0x800) {
30
+ bytes.push(0xc0 | (codePoint >> 6), 0x80 | (codePoint & 0x3f));
31
+ }
32
+ else if (codePoint < 0x10000) {
33
+ bytes.push(0xe0 | (codePoint >> 12), 0x80 | ((codePoint >> 6) & 0x3f), 0x80 | (codePoint & 0x3f));
34
+ }
35
+ else {
36
+ bytes.push(0xf0 | (codePoint >> 18), 0x80 | ((codePoint >> 12) & 0x3f), 0x80 | ((codePoint >> 6) & 0x3f), 0x80 | (codePoint & 0x3f));
37
+ }
38
+ }
39
+ return Uint8Array.from(bytes);
40
+ }
41
+ /**
42
+ * Decode a UTF-8 byte sequence to a string.
43
+ *
44
+ * A portable replacement for `new TextDecoder().decode(bytes)`.
45
+ */
46
+ export function decodeUtf8(bytes) {
47
+ let result = '';
48
+ let index = 0;
49
+ const length = bytes.length;
50
+ while (index < length) {
51
+ const byte1 = bytes[index++] ?? 0;
52
+ let codePoint;
53
+ if (byte1 < 0x80) {
54
+ codePoint = byte1;
55
+ }
56
+ else if ((byte1 & 0xe0) === 0xc0) {
57
+ const byte2 = bytes[index++] ?? 0;
58
+ codePoint = ((byte1 & 0x1f) << 6) | (byte2 & 0x3f);
59
+ }
60
+ else if ((byte1 & 0xf0) === 0xe0) {
61
+ const byte2 = bytes[index++] ?? 0;
62
+ const byte3 = bytes[index++] ?? 0;
63
+ codePoint = ((byte1 & 0x0f) << 12) | ((byte2 & 0x3f) << 6) | (byte3 & 0x3f);
64
+ }
65
+ else {
66
+ const byte2 = bytes[index++] ?? 0;
67
+ const byte3 = bytes[index++] ?? 0;
68
+ const byte4 = bytes[index++] ?? 0;
69
+ codePoint =
70
+ ((byte1 & 0x07) << 18) |
71
+ ((byte2 & 0x3f) << 12) |
72
+ ((byte3 & 0x3f) << 6) |
73
+ (byte4 & 0x3f);
74
+ }
75
+ result += String.fromCodePoint(codePoint);
76
+ }
77
+ return result;
78
+ }