@shayc/open-board-format 0.4.0 → 0.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @shayc/open-board-format
2
2
 
3
+ ## 0.4.2
4
+
5
+ ### Patch Changes
6
+
7
+ - d2beda3: docs: ship the OBF spec mirror (`docs/external/open-board-format.md`) in the npm package
8
+
9
+ The README already points at this file; including it makes that link resolve
10
+ inside `node_modules` and gives offline tooling and coding agents the full
11
+ format semantics (specialty actions, string-list fallback, ID uniqueness)
12
+ that type declarations can't carry.
13
+
14
+ ## 0.4.1
15
+
16
+ ### Patch Changes
17
+
18
+ - e7c9b47: docs: polish public API JSDoc for better IntelliSense
19
+
20
+ - Document every exported type alias (`OBFBoard`, `OBFButton`, …) so hover tooltips show a description instead of just the expanded type
21
+ - Move `ParsedOBZ` field docs from `@property` tags onto the fields themselves so they surface in editors
22
+ - Clarify `data_url` as an API endpoint (not a `data:` URI) and note it is outside the `data` → `path` → `url` fallback chain
23
+ - Document `action` vs `actions` precedence per the OBF spec
24
+ - Correct fflate compression-level scale (0–9), standardize `@throws` phrasing, example quoting, and module headers; tag `buildJsonParseErrorMessage` as `@internal`
25
+
3
26
  ## 0.4.0
4
27
 
5
28
  ### Minor Changes
package/README.md CHANGED
@@ -210,7 +210,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup (Node 22+, Vitest,
210
210
 
211
211
  ## Related
212
212
 
213
- - [Open Board Format specification](https://www.openboardformat.org/docs) — the official standard and format documentation.
213
+ - [Open Board Format specification](https://www.openboardformat.org/docs) — the official standard and format documentation. A 1:1 mirror is kept at [docs/external/open-board-format.md](docs/external/open-board-format.md) for offline reference.
214
214
  - [AAC Board AI](https://github.com/shayc/aac-board-ai) — an offline-first AAC web app built on this package, using on-device browser AI for grammar, tone, and translation ([live app](https://aacboard.app)).
215
215
 
216
216
  ## License
package/dist/index.d.mts CHANGED
@@ -1,62 +1,78 @@
1
1
  import { z } from "zod";
2
2
 
3
3
  //#region src/schema.d.ts
4
- /**
5
- * Open Board Format (OBF) Zod Schemas
6
- *
7
- * These schemas represent the Open Board Format, designed for sharing communication boards and board sets
8
- * between Augmentative and Alternative Communication (AAC) applications.
9
- *
10
- * Official OBF specification: https://www.openboardformat.org/docs
11
- *
12
- * @author Shay Cojocaru
13
- * @license MIT
14
- */
15
4
  /** Unique board-element identifier, coerced to a non-empty string. */
16
5
  declare const OBFIDSchema: z.ZodPipe<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>, z.ZodTransform<string, string | number>>, z.ZodString>;
17
- type OBFID = z.infer<typeof OBFIDSchema>;
18
6
  /**
19
- * Format version of the Open Board Format, e.g., 'open-board-0.1'.
7
+ * Unique board-element identifier, coerced to a non-empty string.
8
+ * See {@link OBFIDSchema}.
20
9
  */
10
+ type OBFID = z.infer<typeof OBFIDSchema>;
11
+ /** Format version of the Open Board Format, e.g., `open-board-0.1`. */
21
12
  declare const OBFFormatVersionSchema: z.ZodString;
13
+ /**
14
+ * Format version of the Open Board Format, e.g., `open-board-0.1`.
15
+ * See {@link OBFFormatVersionSchema}.
16
+ */
22
17
  type OBFFormatVersion = z.infer<typeof OBFFormatVersionSchema>;
23
18
  /**
24
- * Locale identifier, typically a BCP 47 language tag (e.g., 'en', 'en-US', 'fr-CA').
25
- * Not strictly validated — any string is accepted.
19
+ * Locale identifier, typically a BCP 47 language tag (e.g., `en`, `en-US`,
20
+ * `fr-CA`). Not strictly validated — any string is accepted.
26
21
  */
27
22
  declare const OBFLocaleCodeSchema: z.ZodString;
23
+ /**
24
+ * Locale identifier, typically a BCP 47 language tag, e.g., `en`, `en-US`.
25
+ * See {@link OBFLocaleCodeSchema}.
26
+ */
28
27
  type OBFLocaleCode = z.infer<typeof OBFLocaleCodeSchema>;
29
28
  /**
30
- * Key–value pairs mapping symbolic names to their translations in a single locale.
29
+ * Translations for a single locale, keyed by the source string,
30
+ * e.g., `{ "hello": "hola" }`.
31
31
  */
32
32
  declare const OBFLocalizedStringsSchema: z.ZodRecord<z.ZodString, z.ZodString>;
33
+ /**
34
+ * Translations for a single locale, keyed by the source string.
35
+ * See {@link OBFLocalizedStringsSchema}.
36
+ */
33
37
  type OBFLocalizedStrings = z.infer<typeof OBFLocalizedStringsSchema>;
34
38
  /**
35
39
  * Locale-keyed dictionary of translated strings,
36
40
  * e.g., `{ en: { greeting: "Hello" }, fr: { greeting: "Bonjour" } }`.
37
41
  */
38
42
  declare const OBFStringsSchema: z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>>;
43
+ /**
44
+ * Locale-keyed dictionary of translated strings.
45
+ * See {@link OBFStringsSchema}.
46
+ */
39
47
  type OBFStrings = z.infer<typeof OBFStringsSchema>;
40
48
  /**
41
49
  * Spelling action: a `+` prefix followed by the text to append,
42
- * e.g., `"+hello"`.
50
+ * e.g., `+hello`.
43
51
  */
44
52
  declare const OBFSpellingActionSchema: z.ZodString;
53
+ /**
54
+ * Spelling action: a `+` prefix followed by the text to append, e.g., `+hello`.
55
+ * See {@link OBFSpellingActionSchema}.
56
+ */
45
57
  type OBFSpellingAction = z.infer<typeof OBFSpellingActionSchema>;
46
58
  /**
47
- * Specialty action prefixed with `:`, e.g., `":clear"`.
59
+ * Specialty action prefixed with `:`, e.g., `:clear`.
48
60
  * Custom extensions use the `:ext_` prefix.
49
61
  */
50
62
  declare const OBFSpecialtyActionSchema: z.ZodString;
51
- type OBFSpecialtyAction = z.infer<typeof OBFSpecialtyActionSchema>;
52
63
  /**
53
- * Union of spelling and specialty actions that a button can trigger.
64
+ * Specialty action prefixed with `:`, e.g., `:clear`.
65
+ * See {@link OBFSpecialtyActionSchema}.
54
66
  */
67
+ type OBFSpecialtyAction = z.infer<typeof OBFSpecialtyActionSchema>;
68
+ /** Union of spelling and specialty actions that a button can trigger. */
55
69
  declare const OBFButtonActionSchema: z.ZodUnion<readonly [z.ZodString, z.ZodString]>;
56
- type OBFButtonAction = z.infer<typeof OBFButtonActionSchema>;
57
70
  /**
58
- * License terms and attribution for a resource.
71
+ * Union of spelling and specialty actions that a button can trigger.
72
+ * See {@link OBFButtonActionSchema}.
59
73
  */
74
+ type OBFButtonAction = z.infer<typeof OBFButtonActionSchema>;
75
+ /** License terms and attribution for a resource. */
60
76
  declare const OBFLicenseSchema: z.ZodObject<{
61
77
  type: z.ZodString;
62
78
  copyright_notice_url: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodURL, z.ZodLiteral<"">]>, z.ZodTransform<string | undefined, string>>>;
@@ -65,14 +81,22 @@ declare const OBFLicenseSchema: z.ZodObject<{
65
81
  author_url: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodURL, z.ZodLiteral<"">]>, z.ZodTransform<string | undefined, string>>>;
66
82
  author_email: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodEmail, z.ZodLiteral<"">]>, z.ZodTransform<string | undefined, string>>>;
67
83
  }, z.core.$loose>;
84
+ /**
85
+ * License terms and attribution for a resource.
86
+ * See {@link OBFLicenseSchema}.
87
+ */
68
88
  type OBFLicense = z.infer<typeof OBFLicenseSchema>;
69
89
  /**
70
90
  * Common properties for media resources (images and sounds).
71
91
  *
72
92
  * When multiple references are provided, they should be used in the following order:
73
- * 1. data
74
- * 2. path
75
- * 3. url
93
+ * 1. `data`
94
+ * 2. `path`
95
+ * 3. `url`
96
+ *
97
+ * `data_url` is not part of this fallback chain — it is an API endpoint for
98
+ * retrieving information about the resource, not an alternative source of
99
+ * the media bytes.
76
100
  */
77
101
  declare const OBFMediaSchema: z.ZodObject<{
78
102
  id: z.ZodPipe<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>, z.ZodTransform<string, string | number>>, z.ZodString>;
@@ -90,14 +114,20 @@ declare const OBFMediaSchema: z.ZodObject<{
90
114
  author_email: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodEmail, z.ZodLiteral<"">]>, z.ZodTransform<string | undefined, string>>>;
91
115
  }, z.core.$loose>>;
92
116
  }, z.core.$loose>;
93
- type OBFMedia = z.infer<typeof OBFMediaSchema>;
94
117
  /**
95
- * Reference to a symbol in a proprietary symbol set (e.g., SymbolStix).
118
+ * Common properties for media resources (images and sounds).
119
+ * See {@link OBFMediaSchema}.
96
120
  */
121
+ type OBFMedia = z.infer<typeof OBFMediaSchema>;
122
+ /** Reference to a symbol in a proprietary symbol set (e.g., SymbolStix). */
97
123
  declare const OBFSymbolInfoSchema: z.ZodObject<{
98
124
  set: z.ZodString;
99
125
  filename: z.ZodString;
100
126
  }, z.core.$loose>;
127
+ /**
128
+ * Reference to a symbol in a proprietary symbol set.
129
+ * See {@link OBFSymbolInfoSchema}.
130
+ */
101
131
  type OBFSymbolInfo = z.infer<typeof OBFSymbolInfoSchema>;
102
132
  /**
103
133
  * Image resource, extending {@link OBFMediaSchema} with optional
@@ -131,6 +161,10 @@ declare const OBFImageSchema: z.ZodObject<{
131
161
  width: z.ZodOptional<z.ZodNumber>;
132
162
  height: z.ZodOptional<z.ZodNumber>;
133
163
  }, z.core.$loose>;
164
+ /**
165
+ * Image resource with optional symbol and dimension properties.
166
+ * See {@link OBFImageSchema}.
167
+ */
134
168
  type OBFImage = z.infer<typeof OBFImageSchema>;
135
169
  /**
136
170
  * Audio resource. Identical to {@link OBFMediaSchema} — no additional properties.
@@ -151,10 +185,12 @@ declare const OBFSoundSchema: z.ZodObject<{
151
185
  author_email: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodEmail, z.ZodLiteral<"">]>, z.ZodTransform<string | undefined, string>>>;
152
186
  }, z.core.$loose>>;
153
187
  }, z.core.$loose>;
154
- type OBFSound = z.infer<typeof OBFSoundSchema>;
155
188
  /**
156
- * Reference to another board, resolved by ID, path, or URL.
189
+ * Audio resource, identical to {@link OBFMedia}.
190
+ * See {@link OBFSoundSchema}.
157
191
  */
192
+ type OBFSound = z.infer<typeof OBFSoundSchema>;
193
+ /** Reference to another board, resolved by ID, path, or URL. */
158
194
  declare const OBFLoadBoardSchema: z.ZodObject<{
159
195
  id: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>, z.ZodTransform<string | undefined, string | number>>>;
160
196
  name: z.ZodOptional<z.ZodString>;
@@ -162,6 +198,10 @@ declare const OBFLoadBoardSchema: z.ZodObject<{
162
198
  url: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodURL, z.ZodLiteral<"">]>, z.ZodTransform<string | undefined, string>>>;
163
199
  path: z.ZodOptional<z.ZodString>;
164
200
  }, z.core.$loose>;
201
+ /**
202
+ * Reference to another board, resolved by ID, path, or URL.
203
+ * See {@link OBFLoadBoardSchema}.
204
+ */
165
205
  type OBFLoadBoard = z.infer<typeof OBFLoadBoardSchema>;
166
206
  /**
167
207
  * Interactive element on a board, optionally linked to images, sounds, and actions.
@@ -188,6 +228,10 @@ declare const OBFButtonSchema: z.ZodObject<{
188
228
  width: z.ZodOptional<z.ZodNumber>;
189
229
  height: z.ZodOptional<z.ZodNumber>;
190
230
  }, z.core.$loose>;
231
+ /**
232
+ * Interactive element on a board, optionally linked to images, sounds, and
233
+ * actions. See {@link OBFButtonSchema}.
234
+ */
191
235
  type OBFButton = z.infer<typeof OBFButtonSchema>;
192
236
  /**
193
237
  * Row-and-column layout that arranges buttons by their IDs.
@@ -197,6 +241,10 @@ declare const OBFGridSchema: z.ZodObject<{
197
241
  columns: z.ZodNumber;
198
242
  order: z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodPipe<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>, z.ZodTransform<string, string | number>>, z.ZodString>, z.ZodNull]>>>;
199
243
  }, z.core.$loose>;
244
+ /**
245
+ * Row-and-column layout that arranges buttons by their IDs.
246
+ * See {@link OBFGridSchema}.
247
+ */
200
248
  type OBFGrid = z.infer<typeof OBFGridSchema>;
201
249
  /**
202
250
  * Root object of an `.obf` file: the complete definition of a single communication board.
@@ -283,6 +331,10 @@ declare const OBFBoardSchema: z.ZodObject<{
283
331
  }, z.core.$loose>>;
284
332
  strings: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>>>;
285
333
  }, z.core.$loose>;
334
+ /**
335
+ * The complete definition of a single communication board — root object of
336
+ * an `.obf` file. See {@link OBFBoardSchema}.
337
+ */
286
338
  type OBFBoard = z.infer<typeof OBFBoardSchema>;
287
339
  /**
288
340
  * Table of contents for an `.obz` package, mapping resource IDs to their archive paths.
@@ -296,6 +348,10 @@ declare const OBFManifestSchema: z.ZodObject<{
296
348
  sounds: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
297
349
  }, z.core.$loose>;
298
350
  }, z.core.$loose>;
351
+ /**
352
+ * Table of contents for an `.obz` package, mapping resource IDs to their
353
+ * archive paths. See {@link OBFManifestSchema}.
354
+ */
299
355
  type OBFManifest = z.infer<typeof OBFManifestSchema>;
300
356
  //#endregion
301
357
  //#region src/obf.d.ts
@@ -308,7 +364,7 @@ type OBFManifest = z.infer<typeof OBFManifestSchema>;
308
364
  * @param json - The JSON string to parse.
309
365
  * @returns The validated board object.
310
366
  *
311
- * @throws {Error} If the JSON is malformed or does not conform to the OBF schema.
367
+ * @throws {Error} If the JSON is malformed or fails schema validation.
312
368
  */
313
369
  declare function parseOBF(json: string): OBFBoard;
314
370
  /**
@@ -329,7 +385,7 @@ declare function loadOBF(file: File): Promise<OBFBoard>;
329
385
  * @param data - The value to validate.
330
386
  * @returns The validated board object.
331
387
  *
332
- * @throws {Error} If the value does not conform to the OBF schema.
388
+ * @throws {Error} If the value fails schema validation.
333
389
  */
334
390
  declare function validateOBF(data: unknown): OBFBoard;
335
391
  /**
@@ -343,20 +399,22 @@ declare function stringifyOBF(board: OBFBoard): string;
343
399
  //#region src/obz.d.ts
344
400
  /**
345
401
  * Fully extracted contents of an `.obz` archive.
346
- *
347
- * @property manifest - The package table of contents.
348
- * @property boards - Board ID → validated board object.
349
- * @property rootBoard - The package's entry-point board — the one `manifest.root`
350
- * points at, already resolved. Same object as
351
- * `boards.get(rootBoard.id)`.
352
- * @property resources - Archive path → raw bytes for every entry in the archive,
353
- * including `manifest.json` and the `.obf` boards as well as
354
- * media such as images and sounds.
355
402
  */
356
403
  interface ParsedOBZ {
404
+ /** The package's table of contents. */
357
405
  manifest: OBFManifest;
406
+ /** Validated board objects keyed by board ID. */
358
407
  boards: Map<string, OBFBoard>;
408
+ /**
409
+ * The package's entry-point board — the one `manifest.root` points at,
410
+ * already resolved. Same object as `boards.get(rootBoard.id)`.
411
+ */
359
412
  rootBoard: OBFBoard;
413
+ /**
414
+ * Raw bytes for every entry in the archive, keyed by archive path —
415
+ * including `manifest.json` and the `.obf` boards as well as media
416
+ * such as images and sounds.
417
+ */
360
418
  resources: Map<string, Uint8Array>;
361
419
  }
362
420
  /**
@@ -406,9 +464,9 @@ declare function parseManifest(json: string): OBFManifest;
406
464
  * @returns A `Blob` containing the compressed OBZ archive.
407
465
  *
408
466
  * @throws {Error} If `rootBoardId` does not match any of the supplied boards.
409
- * @throws {Error} If two supplied boards share the same id.
467
+ * @throws {Error} If two supplied boards share the same ID.
410
468
  * @throws {Error} If a supplied board fails schema validation.
411
- * @throws {Error} If two boards declare the same media id with conflicting paths.
469
+ * @throws {Error} If two boards declare the same media ID with conflicting paths.
412
470
  * @throws {Error} If a board declares an image or sound `path` with no matching entry in `resources`.
413
471
  * @throws {Error} If a `resources` entry would overwrite the generated `manifest.json` or a board file.
414
472
  */
@@ -441,11 +499,11 @@ type LoadedBoard = {
441
499
  * Detect whether the input is a single OBF board or an OBZ package and load it
442
500
  * accordingly.
443
501
  *
444
- * The input is read once and its leading bytes are sniffed for the ZIP magic
445
- * prefix: a ZIP is treated as an `.obz` package, anything else as an `.obf`
446
- * board. This lets consumers accept either format from a single drag-and-drop,
447
- * file picker, or fetch response without inspecting the file extension or
448
- * re-deriving the OBF-vs-OBZ distinction themselves.
502
+ * Input that begins with the ZIP magic prefix is treated as an `.obz` package;
503
+ * anything else is parsed as an `.obf` board. The input is read once, so
504
+ * consumers can accept either format from a single drag-and-drop, file picker,
505
+ * or fetch response without inspecting the file extension or re-deriving the
506
+ * OBF-vs-OBZ distinction themselves.
449
507
  *
450
508
  * @param input - A `File` handle or `ArrayBuffer` holding `.obf` or `.obz` content.
451
509
  * @returns A discriminated union tagged by `format`.
@@ -456,13 +514,16 @@ type LoadedBoard = {
456
514
  declare function loadBoard(input: File | ArrayBuffer): Promise<LoadedBoard>;
457
515
  //#endregion
458
516
  //#region src/zip.d.ts
517
+ /**
518
+ * Minimal ZIP helpers over fflate: signature sniffing, unzip, and zip.
519
+ */
459
520
  /**
460
521
  * Decompress a ZIP archive into a map of file paths to raw bytes.
461
522
  *
462
523
  * @param archive - The ZIP archive as an `ArrayBuffer`.
463
524
  * @returns A map of file paths to their decompressed content.
464
525
  *
465
- * @throws {Error} If decompression fails.
526
+ * @throws {Error} If the archive is corrupt or cannot be decompressed.
466
527
  */
467
528
  declare function unzip(archive: ArrayBuffer): Promise<Map<string, Uint8Array>>;
468
529
  /**
@@ -475,7 +536,7 @@ declare function unzip(archive: ArrayBuffer): Promise<Map<string, Uint8Array>>;
475
536
  * @param entries - A map of file paths to their content bytes.
476
537
  * @returns The compressed archive as a `Uint8Array`.
477
538
  *
478
- * @throws {Error} If compression fails.
539
+ * @throws {Error} If fflate fails to compress an entry.
479
540
  */
480
541
  declare function zip(entries: Map<string, Uint8Array | ArrayBuffer>): Promise<Uint8Array>;
481
542
  /**