@wemush/wols 0.1.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.
- package/CHANGELOG.md +170 -0
- package/README.md +204 -31
- package/dist/cjs/compact-url.d.ts +150 -0
- package/dist/cjs/compact-url.d.ts.map +1 -0
- package/dist/cjs/compact-url.js +64 -0
- package/dist/cjs/compact-url.js.map +1 -1
- package/dist/cjs/crypto/decrypt.d.ts +110 -0
- package/dist/cjs/crypto/decrypt.d.ts.map +1 -0
- package/dist/cjs/crypto/decrypt.js +521 -90
- package/dist/cjs/crypto/decrypt.js.map +1 -1
- package/dist/cjs/crypto/encrypt.d.ts +132 -0
- package/dist/cjs/crypto/encrypt.d.ts.map +1 -0
- package/dist/cjs/crypto/encrypt.js +288 -105
- package/dist/cjs/crypto/encrypt.js.map +1 -1
- package/dist/cjs/crypto/index.d.ts +12 -0
- package/dist/cjs/crypto/index.d.ts.map +1 -0
- package/dist/cjs/crypto/index.js +15 -2
- package/dist/cjs/crypto/index.js.map +1 -1
- package/dist/cjs/crypto/shared.d.ts +116 -0
- package/dist/cjs/crypto/shared.d.ts.map +1 -0
- package/dist/cjs/crypto/shared.js +238 -0
- package/dist/cjs/crypto/shared.js.map +1 -0
- package/dist/cjs/errors.d.ts +133 -0
- package/dist/cjs/errors.d.ts.map +1 -0
- package/dist/cjs/errors.js +39 -1
- package/dist/cjs/errors.js.map +1 -1
- package/dist/cjs/index.d.ts +29 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +70 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/parser.d.ts +30 -0
- package/dist/cjs/parser.d.ts.map +1 -0
- package/dist/cjs/parser.js +3 -0
- package/dist/cjs/parser.js.map +1 -1
- package/dist/cjs/qr/generate.d.ts +62 -0
- package/dist/cjs/qr/generate.d.ts.map +1 -0
- package/dist/cjs/qr/index.d.ts +10 -0
- package/dist/cjs/qr/index.d.ts.map +1 -0
- package/dist/cjs/specimen.d.ts +96 -0
- package/dist/cjs/specimen.d.ts.map +1 -0
- package/dist/cjs/specimen.js +95 -9
- package/dist/cjs/specimen.js.map +1 -1
- package/dist/cjs/types.d.ts +735 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +327 -3
- package/dist/cjs/types.js.map +1 -1
- package/dist/cjs/utils/cuid.d.ts +46 -0
- package/dist/cjs/utils/cuid.d.ts.map +1 -0
- package/dist/cjs/utils/cuid.js +6 -4
- package/dist/cjs/utils/cuid.js.map +1 -1
- package/dist/cjs/utils/environment.d.ts +137 -0
- package/dist/cjs/utils/environment.d.ts.map +1 -0
- package/dist/cjs/utils/environment.js +206 -0
- package/dist/cjs/utils/environment.js.map +1 -0
- package/dist/cjs/utils/iso8601.d.ts +79 -0
- package/dist/cjs/utils/iso8601.d.ts.map +1 -0
- package/dist/cjs/utils/migration.d.ts +162 -0
- package/dist/cjs/utils/migration.d.ts.map +1 -0
- package/dist/cjs/utils/migration.js +248 -0
- package/dist/cjs/utils/migration.js.map +1 -0
- package/dist/cjs/validator.d.ts +25 -0
- package/dist/cjs/validator.d.ts.map +1 -0
- package/dist/cjs/validator.js +85 -21
- package/dist/cjs/validator.js.map +1 -1
- package/dist/esm/compact-url.js +62 -0
- package/dist/esm/compact-url.js.map +1 -1
- package/dist/esm/crypto/decrypt.js +517 -90
- package/dist/esm/crypto/decrypt.js.map +1 -1
- package/dist/esm/crypto/encrypt.js +284 -105
- package/dist/esm/crypto/encrypt.js.map +1 -1
- package/dist/esm/crypto/index.js +5 -3
- package/dist/esm/crypto/index.js.map +1 -1
- package/dist/esm/crypto/shared.js +222 -0
- package/dist/esm/crypto/shared.js.map +1 -0
- package/dist/esm/errors.js +37 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.js +26 -6
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/parser.js +3 -0
- package/dist/esm/parser.js.map +1 -1
- package/dist/esm/specimen.js +95 -10
- package/dist/esm/specimen.js.map +1 -1
- package/dist/esm/types.js +317 -2
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/utils/cuid.js +6 -4
- package/dist/esm/utils/cuid.js.map +1 -1
- package/dist/esm/utils/environment.js +193 -0
- package/dist/esm/utils/environment.js.map +1 -0
- package/dist/esm/utils/migration.js +237 -0
- package/dist/esm/utils/migration.js.map +1 -0
- package/dist/esm/validator.js +86 -22
- package/dist/esm/validator.js.map +1 -1
- package/dist/types/compact-url.d.ts +46 -0
- package/dist/types/compact-url.d.ts.map +1 -1
- package/dist/types/crypto/decrypt.d.ts +95 -18
- package/dist/types/crypto/decrypt.d.ts.map +1 -1
- package/dist/types/crypto/encrypt.d.ts +114 -14
- package/dist/types/crypto/encrypt.d.ts.map +1 -1
- package/dist/types/crypto/index.d.ts +5 -3
- package/dist/types/crypto/index.d.ts.map +1 -1
- package/dist/types/crypto/shared.d.ts +116 -0
- package/dist/types/crypto/shared.d.ts.map +1 -0
- package/dist/types/errors.d.ts +31 -1
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/index.d.ts +15 -7
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/parser.d.ts.map +1 -1
- package/dist/types/specimen.d.ts +29 -4
- package/dist/types/specimen.d.ts.map +1 -1
- package/dist/types/types.d.ts +483 -17
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils/cuid.d.ts +5 -3
- package/dist/types/utils/cuid.d.ts.map +1 -1
- package/dist/types/utils/environment.d.ts +137 -0
- package/dist/types/utils/environment.d.ts.map +1 -0
- package/dist/types/utils/migration.d.ts +162 -0
- package/dist/types/utils/migration.d.ts.map +1 -0
- package/dist/types/validator.d.ts +1 -1
- package/dist/types/validator.d.ts.map +1 -1
- 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
|