@wemush/wols 1.2.0 → 1.3.0

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 (98) hide show
  1. package/CHANGELOG.md +170 -0
  2. package/README.md +204 -31
  3. package/dist/cjs/compact-url.d.ts +150 -0
  4. package/dist/cjs/compact-url.d.ts.map +1 -0
  5. package/dist/cjs/crypto/decrypt.d.ts +110 -0
  6. package/dist/cjs/crypto/decrypt.d.ts.map +1 -0
  7. package/dist/cjs/crypto/decrypt.js +519 -99
  8. package/dist/cjs/crypto/decrypt.js.map +1 -1
  9. package/dist/cjs/crypto/encrypt.d.ts +132 -0
  10. package/dist/cjs/crypto/encrypt.d.ts.map +1 -0
  11. package/dist/cjs/crypto/encrypt.js +281 -200
  12. package/dist/cjs/crypto/encrypt.js.map +1 -1
  13. package/dist/cjs/crypto/index.d.ts +12 -0
  14. package/dist/cjs/crypto/index.d.ts.map +1 -0
  15. package/dist/cjs/crypto/index.js +15 -2
  16. package/dist/cjs/crypto/index.js.map +1 -1
  17. package/dist/cjs/crypto/shared.d.ts +116 -0
  18. package/dist/cjs/crypto/shared.d.ts.map +1 -0
  19. package/dist/cjs/crypto/shared.js +238 -0
  20. package/dist/cjs/crypto/shared.js.map +1 -0
  21. package/dist/cjs/errors.d.ts +133 -0
  22. package/dist/cjs/errors.d.ts.map +1 -0
  23. package/dist/cjs/errors.js +39 -1
  24. package/dist/cjs/errors.js.map +1 -1
  25. package/dist/cjs/index.d.ts +29 -0
  26. package/dist/cjs/index.d.ts.map +1 -0
  27. package/dist/cjs/index.js +19 -4
  28. package/dist/cjs/index.js.map +1 -1
  29. package/dist/cjs/package.json +3 -0
  30. package/dist/cjs/parser.d.ts +30 -0
  31. package/dist/cjs/parser.d.ts.map +1 -0
  32. package/dist/cjs/qr/generate.d.ts +62 -0
  33. package/dist/cjs/qr/generate.d.ts.map +1 -0
  34. package/dist/cjs/qr/index.d.ts +10 -0
  35. package/dist/cjs/qr/index.d.ts.map +1 -0
  36. package/dist/cjs/specimen.d.ts +96 -0
  37. package/dist/cjs/specimen.d.ts.map +1 -0
  38. package/dist/cjs/specimen.js +84 -11
  39. package/dist/cjs/specimen.js.map +1 -1
  40. package/dist/cjs/types.d.ts +735 -0
  41. package/dist/cjs/types.d.ts.map +1 -0
  42. package/dist/cjs/types.js +65 -5
  43. package/dist/cjs/types.js.map +1 -1
  44. package/dist/cjs/utils/cuid.d.ts +46 -0
  45. package/dist/cjs/utils/cuid.d.ts.map +1 -0
  46. package/dist/cjs/utils/cuid.js +6 -4
  47. package/dist/cjs/utils/cuid.js.map +1 -1
  48. package/dist/cjs/utils/environment.d.ts +137 -0
  49. package/dist/cjs/utils/environment.d.ts.map +1 -0
  50. package/dist/cjs/utils/iso8601.d.ts +79 -0
  51. package/dist/cjs/utils/iso8601.d.ts.map +1 -0
  52. package/dist/cjs/utils/migration.d.ts +162 -0
  53. package/dist/cjs/utils/migration.d.ts.map +1 -0
  54. package/dist/cjs/validator.d.ts +25 -0
  55. package/dist/cjs/validator.d.ts.map +1 -0
  56. package/dist/cjs/validator.js +33 -22
  57. package/dist/cjs/validator.js.map +1 -1
  58. package/dist/esm/crypto/decrypt.js +515 -99
  59. package/dist/esm/crypto/decrypt.js.map +1 -1
  60. package/dist/esm/crypto/encrypt.js +277 -200
  61. package/dist/esm/crypto/encrypt.js.map +1 -1
  62. package/dist/esm/crypto/index.js +5 -3
  63. package/dist/esm/crypto/index.js.map +1 -1
  64. package/dist/esm/crypto/shared.js +222 -0
  65. package/dist/esm/crypto/shared.js.map +1 -0
  66. package/dist/esm/errors.js +37 -0
  67. package/dist/esm/errors.js.map +1 -1
  68. package/dist/esm/index.js +6 -6
  69. package/dist/esm/index.js.map +1 -1
  70. package/dist/esm/specimen.js +84 -12
  71. package/dist/esm/specimen.js.map +1 -1
  72. package/dist/esm/types.js +63 -4
  73. package/dist/esm/types.js.map +1 -1
  74. package/dist/esm/utils/cuid.js +6 -4
  75. package/dist/esm/utils/cuid.js.map +1 -1
  76. package/dist/esm/validator.js +34 -23
  77. package/dist/esm/validator.js.map +1 -1
  78. package/dist/types/crypto/decrypt.d.ts +93 -22
  79. package/dist/types/crypto/decrypt.d.ts.map +1 -1
  80. package/dist/types/crypto/encrypt.d.ts +106 -61
  81. package/dist/types/crypto/encrypt.d.ts.map +1 -1
  82. package/dist/types/crypto/index.d.ts +5 -3
  83. package/dist/types/crypto/index.d.ts.map +1 -1
  84. package/dist/types/crypto/shared.d.ts +116 -0
  85. package/dist/types/crypto/shared.d.ts.map +1 -0
  86. package/dist/types/errors.d.ts +31 -1
  87. package/dist/types/errors.d.ts.map +1 -1
  88. package/dist/types/index.d.ts +7 -7
  89. package/dist/types/index.d.ts.map +1 -1
  90. package/dist/types/specimen.d.ts +29 -4
  91. package/dist/types/specimen.d.ts.map +1 -1
  92. package/dist/types/types.d.ts +265 -40
  93. package/dist/types/types.d.ts.map +1 -1
  94. package/dist/types/utils/cuid.d.ts +5 -3
  95. package/dist/types/utils/cuid.d.ts.map +1 -1
  96. package/dist/types/validator.d.ts +1 -1
  97. package/dist/types/validator.d.ts.map +1 -1
  98. package/package.json +32 -14
@@ -0,0 +1,735 @@
1
+ /**
2
+ * WOLS TypeScript Library - Core Types
3
+ *
4
+ * This module defines all the core types, interfaces, and enums
5
+ * for the WeMush Open Labeling Standard (WOLS) v1.3.0.
6
+ *
7
+ * @packageDocumentation
8
+ * @module @wemush/wols
9
+ */
10
+ /**
11
+ * WOLS specification version implemented by this library.
12
+ */
13
+ export declare const WOLS_VERSION = "1.3.0";
14
+ /**
15
+ * JSON-LD context URL for WOLS specimens.
16
+ */
17
+ export declare const WOLS_CONTEXT = "https://wemush.com/wols/v1";
18
+ /**
19
+ * Branded type pattern for nominal typing.
20
+ * Creates a compile-time unique type that is runtime-compatible with the base type.
21
+ */
22
+ export type Brand<K, T> = K & {
23
+ readonly __brand: T;
24
+ };
25
+ /**
26
+ * Specimen ID type (format: "wemush:{cuid}").
27
+ * This is a branded string type for type safety.
28
+ */
29
+ export type SpecimenId = Brand<string, 'SpecimenId'>;
30
+ /**
31
+ * Create a SpecimenId from a string.
32
+ * Does not validate the format - use isValidSpecimenId for validation.
33
+ */
34
+ export declare function asSpecimenId(id: string): SpecimenId;
35
+ /**
36
+ * Specimen type enum representing cultivation stages.
37
+ */
38
+ export type SpecimenType = 'CULTURE' | 'SPAWN' | 'SUBSTRATE' | 'FRUITING' | 'HARVEST';
39
+ /**
40
+ * Array of valid specimen types for validation.
41
+ */
42
+ export declare const SPECIMEN_TYPES: readonly SpecimenType[];
43
+ /**
44
+ * Reserved type alias with special handling (WOLS v1.3.0).
45
+ *
46
+ * `PRIMORDIA` is a GrowthStage, not a SpecimenType. When it is supplied as a
47
+ * type, it resolves to `FRUITING` and `createSpecimen()` sets
48
+ * `stage: 'PRIMORDIA'` unless a stage was supplied.
49
+ *
50
+ * @since 1.3.0
51
+ */
52
+ export declare const PRIMORDIA_TYPE_ALIAS = "PRIMORDIA";
53
+ /**
54
+ * Register a custom type alias.
55
+ *
56
+ * @param alias - The alias string (case-insensitive, stored uppercase)
57
+ * @param wolsType - The canonical WOLS SpecimenType
58
+ * @throws Error if `alias` is the reserved `PRIMORDIA` alias
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * registerTypeAlias('MYCELIUM_BLOCK', 'SUBSTRATE');
63
+ * ```
64
+ */
65
+ export declare function registerTypeAlias(alias: string, wolsType: SpecimenType): void;
66
+ /**
67
+ * Resolve a type alias to its canonical WOLS type.
68
+ * Returns the input if it's already a valid SpecimenType or no alias exists.
69
+ *
70
+ * @param typeOrAlias - Type string or alias to resolve
71
+ * @returns Canonical SpecimenType or original string if not found
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * resolveTypeAlias('LIQUID_CULTURE') // Returns 'CULTURE'
76
+ * resolveTypeAlias('CULTURE') // Returns 'CULTURE'
77
+ * resolveTypeAlias('unknown') // Returns 'unknown'
78
+ * ```
79
+ *
80
+ * @remarks Maps the type only. `PRIMORDIA` resolves to `FRUITING` here and
81
+ * the PRIMORDIA stage is lost; use {@link resolveTypeAndStage} when building
82
+ * a specimen from user input.
83
+ */
84
+ export declare function resolveTypeAlias(typeOrAlias: string): SpecimenType | string;
85
+ /**
86
+ * Resolve a supplied specimen type (canonical or alias, any case) together
87
+ * with its stage, applying the reserved `PRIMORDIA` rule (WOLS v1.3.0).
88
+ *
89
+ * This is the single source of truth used by `createSpecimen()` and
90
+ * `serializeSpecimen()`. `resolveTypeAlias()` and `mapToWolsType()` map the
91
+ * type only; integrations that map a type and then build a specimen
92
+ * themselves should use this function so the `PRIMORDIA` stage is not lost.
93
+ *
94
+ * - `PRIMORDIA` resolves to `FRUITING`, and `stage` becomes `'PRIMORDIA'`
95
+ * only when no stage was supplied (a supplied stage is never overridden).
96
+ * `primordia` is `true` so callers can warn.
97
+ * - Other aliases resolve the type and leave `stage` unchanged.
98
+ * - Unknown types return `null`.
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * resolveTypeAndStage('primordia') // { type: 'FRUITING', stage: 'PRIMORDIA', primordia: true }
103
+ * resolveTypeAndStage('PRIMORDIA', 'FRUITING') // { type: 'FRUITING', stage: 'FRUITING', primordia: true }
104
+ * resolveTypeAndStage('LC') // { type: 'CULTURE', stage: undefined, primordia: false }
105
+ * ```
106
+ *
107
+ * @since 1.3.0
108
+ */
109
+ export declare function resolveTypeAndStage(type: string, stage?: GrowthStage): {
110
+ type: SpecimenType;
111
+ stage: GrowthStage | undefined;
112
+ primordia: boolean;
113
+ } | null;
114
+ /**
115
+ * Get all registered type aliases.
116
+ *
117
+ * @returns ReadonlyMap of alias -> SpecimenType mappings
118
+ */
119
+ export declare function getTypeAliases(): ReadonlyMap<string, SpecimenType>;
120
+ /**
121
+ * Mapping from WOLS types to common platform type names.
122
+ *
123
+ * This provides a centralized lookup for integrations that need to
124
+ * display user-friendly type names or map to platform-specific values.
125
+ *
126
+ * @since 1.2.0
127
+ */
128
+ export declare const WOLS_TO_PLATFORM_MAP: Readonly<Record<SpecimenType, readonly string[]>>;
129
+ /**
130
+ * Map a platform-specific type to a WOLS SpecimenType.
131
+ *
132
+ * First checks registered aliases, then falls back to direct lookup.
133
+ *
134
+ * @param platformType - Platform-specific type name
135
+ * @returns Matching SpecimenType or null if not found
136
+ *
137
+ * @example
138
+ * ```typescript
139
+ * mapToWolsType('Liquid Culture') // Returns 'CULTURE'
140
+ * mapToWolsType('LC') // Returns 'CULTURE'
141
+ * mapToWolsType('Unknown Type') // Returns null
142
+ * ```
143
+ *
144
+ * @remarks Maps the type only. `PRIMORDIA` maps to `FRUITING` and the
145
+ * PRIMORDIA stage is lost; use {@link resolveTypeAndStage} when building a
146
+ * specimen from user input.
147
+ *
148
+ * @since 1.2.0
149
+ */
150
+ export declare function mapToWolsType(platformType: string): SpecimenType | null;
151
+ /**
152
+ * Get all platform type names for a WOLS SpecimenType.
153
+ *
154
+ * @param wolsType - WOLS SpecimenType
155
+ * @returns Array of platform type names
156
+ *
157
+ * @example
158
+ * ```typescript
159
+ * mapFromWolsType('CULTURE')
160
+ * // Returns ['Liquid Culture', 'LC', 'Agar', 'Petri', 'Slant', 'Culture Plate']
161
+ * ```
162
+ *
163
+ * @since 1.2.0
164
+ */
165
+ export declare function mapFromWolsType(wolsType: SpecimenType): readonly string[];
166
+ /**
167
+ * Register a custom platform type mapping.
168
+ *
169
+ * This adds to both the alias registry (for resolveTypeAlias) and
170
+ * can be used for custom platform integrations.
171
+ *
172
+ * @param platformType - Platform-specific type name
173
+ * @param wolsType - WOLS SpecimenType to map to
174
+ *
175
+ * @example
176
+ * ```typescript
177
+ * registerPlatformType('Mycelium Block', 'SUBSTRATE');
178
+ * mapToWolsType('Mycelium Block') // Returns 'SUBSTRATE'
179
+ * ```
180
+ *
181
+ * @since 1.2.0
182
+ */
183
+ export declare function registerPlatformType(platformType: string, wolsType: SpecimenType): void;
184
+ /**
185
+ * Growth stage enum representing specimen lifecycle.
186
+ *
187
+ * v1.2.0 introduces research-grade lifecycle tracking with 7 stages:
188
+ * - INOCULATION: Initial introduction of spawn/culture to substrate
189
+ * - INCUBATION: Post-inoculation rest period before visible growth (v1.2.0)
190
+ * - COLONIZATION: Active mycelial growth through substrate
191
+ * - PRIMORDIA: Pin initiation and early fruit body formation (v1.2.0)
192
+ * - FRUITING: Active fruit body development and maturation
193
+ * - SENESCENCE: Declining productivity, end-of-life phase (v1.2.0)
194
+ * - HARVEST: Mushrooms harvested and processed
195
+ */
196
+ export type GrowthStage = 'INOCULATION' | 'INCUBATION' | 'COLONIZATION' | 'PRIMORDIA' | 'FRUITING' | 'SENESCENCE' | 'HARVEST';
197
+ /**
198
+ * Array of valid growth stages for validation.
199
+ * Ordered by typical lifecycle progression.
200
+ */
201
+ export declare const GROWTH_STAGES: readonly GrowthStage[];
202
+ /**
203
+ * Output format for generation normalization.
204
+ *
205
+ * - 'preserve': Keep original format
206
+ * - 'filial': Normalize to F{n} format (F1, F2, etc.)
207
+ * - 'numeric': Normalize to numeric format (1, 2, etc.)
208
+ *
209
+ * @since 1.2.0
210
+ */
211
+ export type GenerationFormat = 'preserve' | 'filial' | 'numeric';
212
+ /**
213
+ * Normalize a generation string to the specified format.
214
+ *
215
+ * Accepts multiple input formats:
216
+ * - Filial: F1, F2, F3, etc.
217
+ * - Parental: P or P1
218
+ * - Generational: G1, G2, G3, etc.
219
+ * - Numeric: 1, 2, 3, etc.
220
+ *
221
+ * @param generation - The generation string to normalize
222
+ * @param format - Target format (default: 'preserve')
223
+ * @returns Normalized generation string, or original if not parseable
224
+ *
225
+ * @example
226
+ * ```typescript
227
+ * normalizeGeneration('G2', 'filial') // Returns 'F2'
228
+ * normalizeGeneration('3', 'filial') // Returns 'F3'
229
+ * normalizeGeneration('F1', 'numeric') // Returns '1'
230
+ * normalizeGeneration('P', 'filial') // Returns 'P'
231
+ * ```
232
+ *
233
+ * @since 1.2.0
234
+ */
235
+ export declare function normalizeGeneration(generation: string, format?: GenerationFormat): string;
236
+ /**
237
+ * Check if a generation string is valid.
238
+ *
239
+ * @param generation - The generation string to validate
240
+ * @returns True if valid, false otherwise
241
+ *
242
+ * @since 1.2.0
243
+ */
244
+ export declare function isValidGeneration(generation: string): boolean;
245
+ /**
246
+ * Metadata for specimen round-trip preservation.
247
+ *
248
+ * The `_meta` namespace is reserved for implementation-specific metadata
249
+ * that should be preserved through import/export operations but is not
250
+ * part of the core WOLS specification.
251
+ *
252
+ * @since 1.2.0
253
+ */
254
+ export interface SpecimenMeta {
255
+ /** Original ID from source system before WOLS normalization */
256
+ sourceId?: string;
257
+ /** ISO 8601 timestamp when specimen was imported */
258
+ importedAt?: string;
259
+ /** Identifier of the source system (e.g., "wemush-platform", "mycology-db") */
260
+ sourceSystem?: string;
261
+ /** Schema version used by source system */
262
+ schemaVersion?: string;
263
+ /** Extensible: additional implementation-specific fields */
264
+ [key: string]: unknown;
265
+ }
266
+ /**
267
+ * Strain information for a specimen.
268
+ */
269
+ export interface Strain {
270
+ /** Strain name or identifier */
271
+ name: string;
272
+ /** Filial generation (e.g., "F1", "F2", "P") */
273
+ generation?: string;
274
+ /**
275
+ * Culture-to-culture subculture count. The original culture is `0`.
276
+ * Expansion to grain or substrate does not increment it.
277
+ */
278
+ clonalGeneration?: number;
279
+ /**
280
+ * Total transfers of any kind from the original culture of this strain
281
+ * (subculture, culture to grain, grain to grain, grain to substrate).
282
+ * The original culture is `0`. Non-negative integer; SHOULD be at least
283
+ * `clonalGeneration` when both are present.
284
+ *
285
+ * @since 1.3.0
286
+ */
287
+ transferDepth?: number;
288
+ /** Parent specimen ID reference */
289
+ lineage?: string;
290
+ /** Origin: "spore", "tissue", "agar", "liquid" */
291
+ source?: string;
292
+ }
293
+ /**
294
+ * Primary specimen entity following the WOLS v1.3.0 JSON-LD schema.
295
+ */
296
+ export interface Specimen {
297
+ /** JSON-LD context URL */
298
+ '@context': typeof WOLS_CONTEXT;
299
+ /** JSON-LD entity type */
300
+ '@type': 'Specimen';
301
+ /** Unique specimen ID (format: wemush:{cuid}) */
302
+ id: SpecimenId;
303
+ /** WOLS specification version */
304
+ version: string;
305
+ /** Cultivation stage type */
306
+ type: SpecimenType;
307
+ /** Scientific species name */
308
+ species: string;
309
+ /** Strain/genetic information */
310
+ strain?: Strain;
311
+ /** Current growth stage */
312
+ stage?: GrowthStage;
313
+ /** Creation timestamp (ISO 8601) */
314
+ created?: string;
315
+ /** Batch identifier */
316
+ batch?: string;
317
+ /** Organization ID */
318
+ organization?: string;
319
+ /** Creator user ID */
320
+ creator?: string;
321
+ /** Custom vendor-specific fields */
322
+ custom?: Record<string, unknown>;
323
+ /** Cryptographic signature */
324
+ signature?: string;
325
+ /**
326
+ * Implementation metadata (preserved through operations).
327
+ * @since 1.2.0
328
+ */
329
+ _meta?: SpecimenMeta;
330
+ }
331
+ /**
332
+ * Input type for createSpecimen().
333
+ * Strain can be a string (auto-expanded) or full Strain object.
334
+ * Type accepts aliases (e.g., 'LIQUID_CULTURE' -> 'CULTURE') via resolveTypeAlias().
335
+ */
336
+ export interface SpecimenInput {
337
+ /**
338
+ * Cultivation stage type. Accepts aliases case-insensitively
339
+ * (e.g. 'LIQUID_CULTURE' -> 'CULTURE'). `PRIMORDIA` resolves to `FRUITING`
340
+ * and sets `stage: 'PRIMORDIA'` when no stage is supplied (v1.3.0).
341
+ */
342
+ type: SpecimenType | string;
343
+ /** Scientific species name */
344
+ species: string;
345
+ /** String shorthand expands to { name: string } */
346
+ strain?: string | Strain;
347
+ /** Current growth stage */
348
+ stage?: GrowthStage;
349
+ /**
350
+ * Creation timestamp (ISO 8601 string, or a Date converted with toISOString()).
351
+ * Omitted from the specimen when not supplied.
352
+ *
353
+ * @since 1.3.0
354
+ */
355
+ created?: string | Date;
356
+ /** Batch identifier */
357
+ batch?: string;
358
+ /** Organization ID */
359
+ organization?: string;
360
+ /** Creator user ID */
361
+ creator?: string;
362
+ /** Custom vendor-specific fields */
363
+ custom?: Record<string, unknown>;
364
+ /**
365
+ * Implementation metadata (preserved through operations).
366
+ * @since 1.2.0
367
+ */
368
+ _meta?: SpecimenMeta;
369
+ }
370
+ /**
371
+ * Options for createSpecimen().
372
+ *
373
+ * @since 1.3.0
374
+ */
375
+ export interface CreateSpecimenOptions {
376
+ /**
377
+ * Receives non-fatal warnings raised while creating the specimen, such as
378
+ * `PRIMORDIA` supplied as a type. When omitted, warnings go to console.warn.
379
+ */
380
+ onWarning?: (warning: ValidationWarning) => void;
381
+ }
382
+ /**
383
+ * Import error types for use in ParseResult.
384
+ * Circular dependency avoided by using forward reference.
385
+ */
386
+ import type { WolsDecryptionError, WolsError } from './errors.js';
387
+ /**
388
+ * Parse result discriminated union.
389
+ * @template T The type of data on success
390
+ */
391
+ export type ParseResult<T> = {
392
+ success: true;
393
+ data: T;
394
+ } | {
395
+ success: false;
396
+ error: WolsError;
397
+ };
398
+ /**
399
+ * Validation error detail.
400
+ */
401
+ export interface ValidationError {
402
+ /** JSON path to invalid field (e.g., "strain.name") */
403
+ path: string;
404
+ /** Stable error code */
405
+ code: string;
406
+ /** Human-readable error message */
407
+ message: string;
408
+ }
409
+ /**
410
+ * Validation warning detail.
411
+ */
412
+ export interface ValidationWarning {
413
+ /** JSON path to field */
414
+ path: string;
415
+ /** Warning code */
416
+ code: string;
417
+ /** Human-readable warning message */
418
+ message: string;
419
+ /** Suggested fix */
420
+ suggestion?: string;
421
+ }
422
+ /**
423
+ * Validation result with errors and warnings.
424
+ */
425
+ export interface ValidationResult {
426
+ /** Whether validation passed */
427
+ valid: boolean;
428
+ /** Validation errors (causes valid: false) */
429
+ errors: ValidationError[];
430
+ /** Validation warnings (informational) */
431
+ warnings: ValidationWarning[];
432
+ }
433
+ /**
434
+ * ID validation mode for flexible ID format support.
435
+ *
436
+ * @since 1.2.0
437
+ */
438
+ export type IdValidationMode = 'any' | 'strict' | 'ulid' | 'uuid';
439
+ /**
440
+ * Custom ID validator function signature.
441
+ *
442
+ * @since 1.2.0
443
+ */
444
+ export type IdValidator = (id: string) => boolean;
445
+ /**
446
+ * Validation options.
447
+ */
448
+ export interface ValidationOptions {
449
+ /** Allow unknown fields in custom namespace */
450
+ allowUnknownFields?: boolean;
451
+ /** Validation strictness level */
452
+ level?: 'strict' | 'lenient';
453
+ /**
454
+ * ID format validation mode. Every mode requires the `wemush:` prefix.
455
+ *
456
+ * - 'any': any non-empty suffix after `wemush:` (default; required by WOLS)
457
+ * - 'strict': opt-in CUID-style check, suffix must match `[a-z0-9]+`
458
+ * - 'ulid': opt-in check, suffix must be a valid ULID
459
+ * - 'uuid': opt-in check, suffix must be a valid UUID
460
+ *
461
+ * @since 1.2.0
462
+ * @since 1.3.0 Default changed from 'strict' to 'any'
463
+ */
464
+ idMode?: IdValidationMode;
465
+ /**
466
+ * Custom ID validator function.
467
+ * If provided, overrides idMode.
468
+ *
469
+ * @since 1.2.0
470
+ */
471
+ customIdValidator?: IdValidator;
472
+ }
473
+ /**
474
+ * Reference data extracted from a compact URL.
475
+ *
476
+ * Contains the minimal information encoded in the compact format.
477
+ * Full specimen data must be retrieved via API lookup using the id.
478
+ */
479
+ export interface SpecimenRef {
480
+ /** Full specimen ID with wemush: prefix */
481
+ id: string;
482
+ /** Species name (resolved from code if known) */
483
+ species: string;
484
+ /** Specimen type (CULTURE, SPAWN, etc.) */
485
+ type?: SpecimenType;
486
+ /** Growth stage */
487
+ stage?: GrowthStage;
488
+ /** Unix timestamp of creation */
489
+ timestamp?: number;
490
+ /** Batch identifier */
491
+ batch?: string;
492
+ /** Strain information */
493
+ strain?: {
494
+ name: string;
495
+ generation?: string;
496
+ };
497
+ /** Version (from URL path) */
498
+ version: string;
499
+ }
500
+ /**
501
+ * QR code generation options.
502
+ */
503
+ export interface QRCodeOptions {
504
+ /** Width/height in pixels (default: 300) */
505
+ size?: number;
506
+ /** Error correction level (default: 'M') */
507
+ errorCorrection?: 'L' | 'M' | 'Q' | 'H';
508
+ /** Encoding format (default: 'embedded') */
509
+ format?: 'embedded' | 'compact';
510
+ /** Quiet zone modules (default: 1) */
511
+ margin?: number;
512
+ /** QR code colors */
513
+ color?: {
514
+ dark?: string;
515
+ light?: string;
516
+ };
517
+ }
518
+ /**
519
+ * Key derivation function recorded in an encrypted label (WOLS v1.3.0).
520
+ *
521
+ * - 'pbkdf2': key derived from a passphrase with PBKDF2-HMAC-SHA256 and a per-label salt
522
+ * - 'raw': a 32-byte AES-256 key shared out of band
523
+ *
524
+ * @since 1.3.0
525
+ */
526
+ export type EncryptionKdf = 'pbkdf2' | 'raw';
527
+ /**
528
+ * Raw AES-256 key material: 32 bytes, or an AES-GCM CryptoKey of length 256.
529
+ * Selects `kdf=raw`.
530
+ *
531
+ * @since 1.3.0
532
+ */
533
+ export type RawEncryptionKey = Uint8Array | ArrayBuffer | CryptoKey;
534
+ /**
535
+ * Encryption options.
536
+ *
537
+ * A passphrase string selects `kdf=pbkdf2` (fresh random 16-byte salt per label).
538
+ * Raw key bytes or a CryptoKey select `kdf=raw`.
539
+ *
540
+ * @since 1.2.0 Added `iterations` option for configurable PBKDF2 strength
541
+ * @since 1.3.0 Raw key bytes accepted; per-label random salt; minimum 100,000 iterations
542
+ */
543
+ export interface EncryptionOptions {
544
+ /** Passphrase (kdf=pbkdf2) or raw 32-byte key / AES-GCM CryptoKey (kdf=raw) */
545
+ key: string | RawEncryptionKey;
546
+ /**
547
+ * Fields to encrypt in place instead of the whole specimen.
548
+ *
549
+ * @deprecated Use {@link encryptFields} (typed result). Kept for 1.2.x
550
+ * compatibility; the result is the specimen with those fields replaced,
551
+ * not an {@link EncryptedSpecimen} wrapper.
552
+ */
553
+ fields?: string[];
554
+ /**
555
+ * PBKDF2 iteration count for passphrase keys. Ignored for raw keys.
556
+ *
557
+ * - Default: 600,000 (WOLS 1.3.0)
558
+ * - Allowed when generating: 100,000 to 10,000,000; other values throw
559
+ *
560
+ * @since 1.2.0
561
+ */
562
+ iterations?: number;
563
+ }
564
+ /**
565
+ * Options for `encryptFields()` (field-level encryption).
566
+ *
567
+ * @since 1.3.0
568
+ */
569
+ export interface FieldEncryptionOptions<K extends keyof Specimen = keyof Specimen> {
570
+ /** Passphrase (kdf=pbkdf2) or raw 32-byte key / AES-GCM CryptoKey (kdf=raw) */
571
+ key: string | RawEncryptionKey;
572
+ /**
573
+ * Names of specimen fields to encrypt. Each must be present on the specimen.
574
+ * `@context`, `@type`, `id`, `version`, `type` and `species` identify the
575
+ * label and cannot be encrypted.
576
+ */
577
+ fields: readonly K[];
578
+ /** PBKDF2 iteration count for passphrase keys (default 600,000; 100,000 to 10,000,000) */
579
+ iterations?: number;
580
+ }
581
+ /**
582
+ * Field-level encrypted value (WOLS v1.3.0). Replaces the value of an
583
+ * encrypted field inside an embedded label. The plaintext is the UTF-8 JSON
584
+ * serialization of the original field value.
585
+ *
586
+ * @since 1.3.0
587
+ */
588
+ export interface EncryptedField {
589
+ encrypted: true;
590
+ algorithm: 'AES-256-GCM';
591
+ /** Ciphertext with the 16-byte GCM tag appended, base64url without padding */
592
+ payload: string;
593
+ /** 12-byte IV, base64url without padding */
594
+ iv: string;
595
+ kdf: EncryptionKdf;
596
+ /** PBKDF2 salt, base64url without padding (kdf=pbkdf2 only) */
597
+ salt?: string;
598
+ /** PBKDF2 iteration count (kdf=pbkdf2 only) */
599
+ iterations?: number;
600
+ }
601
+ /**
602
+ * A specimen in which some field values have been replaced by
603
+ * {@link EncryptedField} objects (output of `encryptFields()`).
604
+ *
605
+ * Each encrypted value is bound to its label and field by AES-GCM additional
606
+ * data (`"{id}\n{field}"`, WOLS 1.3.0), so a value moved to another field or
607
+ * copied into another label fails authentication.
608
+ *
609
+ * @since 1.3.0
610
+ */
611
+ export type FieldEncryptedSpecimen = {
612
+ [K in keyof Specimen]: Specimen[K] | EncryptedField;
613
+ };
614
+ /**
615
+ * Precise result of `encryptFields(specimen, { fields })`: the named fields
616
+ * are {@link EncryptedField}s and every other field keeps its cleartext type.
617
+ * Assignable to {@link FieldEncryptedSpecimen}.
618
+ *
619
+ * @since 1.3.0
620
+ */
621
+ export type FieldEncryptedSpecimenOf<K extends keyof Specimen> = Omit<Specimen, K> & {
622
+ [P in K]-?: EncryptedField;
623
+ };
624
+ /**
625
+ * Encrypted specimen.
626
+ *
627
+ * Objects produced by v1.3.0 always carry `kdf`, `id` and `url`; `url` is the
628
+ * `wemush://v1/encrypted/{id}?e=&iv=&kdf=...` wire format to put in a QR code.
629
+ * Objects without `kdf` are legacy (pre-1.3.0) and decrypt only with
630
+ * `allowLegacy: true`.
631
+ *
632
+ * @since 1.2.0 Added `cryptoVersion` and `iterations` fields for migration support
633
+ * @since 1.3.0 Added `kdf`, `salt`, `id`, `url` and `signature`
634
+ */
635
+ export interface EncryptedSpecimen {
636
+ /** Flag indicating this is encrypted */
637
+ encrypted: true;
638
+ /** Ciphertext with the 16-byte GCM tag appended, base64url without padding */
639
+ payload: string;
640
+ /** 12-byte IV, base64url without padding */
641
+ iv: string;
642
+ /** Encryption algorithm used */
643
+ algorithm: 'AES-256-GCM';
644
+ /**
645
+ * Key derivation function. Absent only on legacy (pre-1.3.0) payloads.
646
+ * @since 1.3.0
647
+ */
648
+ kdf?: EncryptionKdf;
649
+ /**
650
+ * PBKDF2 salt, base64url without padding (kdf=pbkdf2 only).
651
+ * @since 1.3.0
652
+ */
653
+ salt?: string;
654
+ /** PBKDF2 iteration count (kdf=pbkdf2, or legacy payloads) */
655
+ iterations?: number;
656
+ /**
657
+ * Full specimen ID (`wemush:...`). Cleartext; must match the decrypted specimen.
658
+ * @since 1.3.0
659
+ */
660
+ id?: string;
661
+ /**
662
+ * Encrypted-format URL for QR encoding.
663
+ * @since 1.3.0
664
+ */
665
+ url?: string;
666
+ /**
667
+ * Detached `sig` parameter from an encrypted URL. Preserved, not verified.
668
+ * @since 1.3.0
669
+ */
670
+ signature?: string;
671
+ /** @deprecated Unused; the GCM tag is appended to `payload`. Removed in 2.0. */
672
+ tag?: string;
673
+ /** @deprecated Legacy (pre-1.3.0) crypto schema marker. Removed in 2.0. */
674
+ cryptoVersion?: 'v1';
675
+ }
676
+ /**
677
+ * Decryption options.
678
+ *
679
+ * @since 1.3.0 Raw key bytes accepted; `allowLegacy` added
680
+ */
681
+ export interface DecryptionOptions {
682
+ /** Passphrase (kdf=pbkdf2) or raw 32-byte key / AES-GCM CryptoKey (kdf=raw) */
683
+ key: string | RawEncryptionKey;
684
+ /**
685
+ * Decrypt legacy payloads that have no `kdf` (pre-1.3.0, fixed library salt).
686
+ * Off by default. Legacy payloads are never generated.
687
+ *
688
+ * @since 1.3.0
689
+ */
690
+ allowLegacy?: boolean;
691
+ /**
692
+ * Largest PBKDF2 iteration count accepted from a label. The count comes
693
+ * from the (untrusted) label, so this bounds the CPU an attacker-supplied
694
+ * label can cost. Labels above it fail with `excessive-iterations`.
695
+ *
696
+ * - Default and maximum: 10,000,000 (WOLS 1.3.0). It may be set lower,
697
+ * never higher. `decryptFields` applies it to the label's total.
698
+ *
699
+ * @since 1.3.0
700
+ */
701
+ maxIterations?: number;
702
+ }
703
+ /**
704
+ * Category of a decryption failure, available as `error.reason` on
705
+ * {@link WolsDecryptionError} (and as `error.details.reason`).
706
+ *
707
+ * @since 1.3.0
708
+ */
709
+ export type DecryptionFailureReason = 'invalid-url' | 'invalid-encoding' | 'missing-parameter' | 'invalid-parameter' | 'unexpected-parameter' | 'unsupported-kdf' | 'insufficient-iterations' | 'excessive-iterations' | 'invalid-key' | 'legacy-format' | 'authentication-failed' | 'invalid-plaintext' | 'id-mismatch' | 'identifying-field' | 'unsupported-environment';
710
+ /**
711
+ * Result of a decryption call: like {@link ParseResult}, but a failure always
712
+ * carries a {@link WolsDecryptionError} with a typed `reason`.
713
+ *
714
+ * @since 1.3.0
715
+ */
716
+ export type DecryptResult<T> = {
717
+ success: true;
718
+ data: T;
719
+ } | {
720
+ success: false;
721
+ error: WolsDecryptionError;
722
+ };
723
+ /**
724
+ * Type guard to check if a value is a valid SpecimenType.
725
+ */
726
+ export declare function isSpecimenType(value: unknown): value is SpecimenType;
727
+ /**
728
+ * Type guard to check if a value is a valid GrowthStage.
729
+ */
730
+ export declare function isGrowthStage(value: unknown): value is GrowthStage;
731
+ /**
732
+ * Type guard to check if a value is a Strain object.
733
+ */
734
+ export declare function isStrain(value: unknown): value is Strain;
735
+ //# sourceMappingURL=types.d.ts.map