@wemush/wols 0.1.0 → 1.2.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 (58) hide show
  1. package/dist/cjs/compact-url.js +64 -0
  2. package/dist/cjs/compact-url.js.map +1 -1
  3. package/dist/cjs/crypto/decrypt.js +16 -5
  4. package/dist/cjs/crypto/decrypt.js.map +1 -1
  5. package/dist/cjs/crypto/encrypt.js +111 -9
  6. package/dist/cjs/crypto/encrypt.js.map +1 -1
  7. package/dist/cjs/index.js +53 -1
  8. package/dist/cjs/index.js.map +1 -1
  9. package/dist/cjs/parser.js +3 -0
  10. package/dist/cjs/parser.js.map +1 -1
  11. package/dist/cjs/specimen.js +14 -1
  12. package/dist/cjs/specimen.js.map +1 -1
  13. package/dist/cjs/types.js +267 -3
  14. package/dist/cjs/types.js.map +1 -1
  15. package/dist/cjs/utils/environment.js +206 -0
  16. package/dist/cjs/utils/environment.js.map +1 -0
  17. package/dist/cjs/utils/migration.js +248 -0
  18. package/dist/cjs/utils/migration.js.map +1 -0
  19. package/dist/cjs/validator.js +62 -9
  20. package/dist/cjs/validator.js.map +1 -1
  21. package/dist/esm/compact-url.js +62 -0
  22. package/dist/esm/compact-url.js.map +1 -1
  23. package/dist/esm/crypto/decrypt.js +16 -5
  24. package/dist/esm/crypto/decrypt.js.map +1 -1
  25. package/dist/esm/crypto/encrypt.js +111 -9
  26. package/dist/esm/crypto/encrypt.js.map +1 -1
  27. package/dist/esm/index.js +21 -1
  28. package/dist/esm/index.js.map +1 -1
  29. package/dist/esm/parser.js +3 -0
  30. package/dist/esm/parser.js.map +1 -1
  31. package/dist/esm/specimen.js +15 -2
  32. package/dist/esm/specimen.js.map +1 -1
  33. package/dist/esm/types.js +258 -2
  34. package/dist/esm/types.js.map +1 -1
  35. package/dist/esm/utils/environment.js +193 -0
  36. package/dist/esm/utils/environment.js.map +1 -0
  37. package/dist/esm/utils/migration.js +237 -0
  38. package/dist/esm/utils/migration.js.map +1 -0
  39. package/dist/esm/validator.js +62 -9
  40. package/dist/esm/validator.js.map +1 -1
  41. package/dist/types/compact-url.d.ts +46 -0
  42. package/dist/types/compact-url.d.ts.map +1 -1
  43. package/dist/types/crypto/decrypt.d.ts +6 -0
  44. package/dist/types/crypto/decrypt.d.ts.map +1 -1
  45. package/dist/types/crypto/encrypt.d.ts +56 -1
  46. package/dist/types/crypto/encrypt.d.ts.map +1 -1
  47. package/dist/types/index.d.ts +10 -2
  48. package/dist/types/index.d.ts.map +1 -1
  49. package/dist/types/parser.d.ts.map +1 -1
  50. package/dist/types/specimen.d.ts.map +1 -1
  51. package/dist/types/types.d.ts +246 -5
  52. package/dist/types/types.d.ts.map +1 -1
  53. package/dist/types/utils/environment.d.ts +137 -0
  54. package/dist/types/utils/environment.d.ts.map +1 -0
  55. package/dist/types/utils/migration.d.ts +162 -0
  56. package/dist/types/utils/migration.d.ts.map +1 -0
  57. package/dist/types/validator.d.ts.map +1 -1
  58. package/package.json +1 -1
@@ -4,16 +4,90 @@
4
4
  * Provides AES-256-GCM encryption for specimens using Web Crypto API.
5
5
  * Supports both password-based (PBKDF2) and raw CryptoKey encryption.
6
6
  *
7
+ * ## Security Model
8
+ *
9
+ * This module implements a **deterministic key derivation** model optimized for:
10
+ * - Cross-environment compatibility (browser + Node.js)
11
+ * - Offline decryption (no external key storage needed)
12
+ * - Self-describing payloads (iterations stored in output)
13
+ *
14
+ * ### Password vs CryptoKey
15
+ *
16
+ * | Input Type | Use Case | Security Level |
17
+ * |------------|----------|----------------|
18
+ * | `string` (password) | Simple sharing, QR codes | Good (depends on password strength) |
19
+ * | `CryptoKey` | Server-side, key management systems | Best (full control over key material) |
20
+ *
21
+ * ### Security Considerations
22
+ *
23
+ * 1. **Fixed Salt**: The PBKDF2 salt is fixed per crypto version. This is intentional
24
+ * for deterministic cross-environment key derivation. Password strength is critical.
25
+ *
26
+ * 2. **Iteration Count**: Default 100,000 iterations. Can be increased via `iterations`
27
+ * option for higher security (at cost of performance).
28
+ *
29
+ * 3. **Random IV**: Each encryption uses a cryptographically random 96-bit IV,
30
+ * ensuring identical plaintexts produce different ciphertexts.
31
+ *
32
+ * 4. **Authenticated Encryption**: AES-256-GCM provides both confidentiality and
33
+ * integrity - tampered ciphertext will fail decryption.
34
+ *
35
+ * ## Future Specification Updates (v1.2.1+)
36
+ *
37
+ * TODO(wols-spec): The following enhancements should be documented in the upstream
38
+ * WOLS specification (wemush/open-standard/SPECIFICATION.md) in a future release:
39
+ *
40
+ * 1. **EncryptedSpecimen schema**: Define the `cryptoVersion` and `iterations` fields
41
+ * in the encrypted payload format section.
42
+ *
43
+ * 2. **Configurable iterations**: Document the `iterations` option in EncryptionOptions
44
+ * with recommended ranges (100k-600k) and minimum floor (10k).
45
+ *
46
+ * 3. **Self-describing payloads**: Explain that v1.2.0+ payloads include derivation
47
+ * parameters for forward-compatible decryption.
48
+ *
49
+ * 4. **Security guidance**: Add a security considerations section covering:
50
+ * - Fixed salt tradeoffs (determinism vs uniqueness)
51
+ * - Password strength requirements
52
+ * - When to use CryptoKey vs password
53
+ * - Optional random salt for high-security scenarios (future v2)
54
+ *
7
55
  * @module crypto/encrypt
8
56
  */
9
57
  import { serializeSpecimen } from '../specimen.js';
10
58
  // =============================================================================
11
59
  // CONSTANTS
12
60
  // =============================================================================
13
- /** Salt for PBKDF2 key derivation (fixed for deterministic key from same password) */
61
+ /**
62
+ * Salt for PBKDF2 key derivation.
63
+ *
64
+ * DESIGN DECISION: This salt is intentionally fixed (not random) for deterministic
65
+ * key derivation. This allows:
66
+ *
67
+ * 1. **Cross-environment compatibility**: Same password produces same key in
68
+ * browser (Web Crypto API) and server (Node.js crypto), enabling encrypted
69
+ * specimens to be decrypted in either environment.
70
+ *
71
+ * 2. **Offline decryption**: No need to store/transmit the salt alongside the
72
+ * encrypted payload - the salt is known from the library version.
73
+ *
74
+ * 3. **Version-aware migration**: The salt includes a version identifier
75
+ * (@wemush/wols/crypto/v1) allowing future versions to use different salts
76
+ * while maintaining backward compatibility for decryption.
77
+ *
78
+ * SECURITY NOTE: The fixed salt means password security depends entirely on
79
+ * password strength. Users should use strong, unique passwords. For maximum
80
+ * security, use a CryptoKey directly instead of a password string.
81
+ *
82
+ * @internal
83
+ */
14
84
  const PBKDF2_SALT = new TextEncoder().encode('@wemush/wols/crypto/v1');
15
- /** Number of PBKDF2 iterations */
16
- const PBKDF2_ITERATIONS = 100_000;
85
+ /** Default number of PBKDF2 iterations */
86
+ const DEFAULT_PBKDF2_ITERATIONS = 100_000;
87
+ /** Minimum allowed PBKDF2 iterations (security floor) */
88
+ const MIN_PBKDF2_ITERATIONS = 10_000;
89
+ /** Current crypto schema version */
90
+ const CRYPTO_VERSION = 'v1';
17
91
  /** IV length in bytes (96 bits = 12 bytes recommended for GCM) */
18
92
  const IV_LENGTH = 12;
19
93
  // =============================================================================
@@ -30,10 +104,21 @@ function toBase64Url(buffer) {
30
104
  }
31
105
  return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
32
106
  }
107
+ /**
108
+ * Validate and normalize iteration count
109
+ * @throws Error if iterations below minimum
110
+ */
111
+ function validateIterations(iterations) {
112
+ const count = iterations ?? DEFAULT_PBKDF2_ITERATIONS;
113
+ if (count < MIN_PBKDF2_ITERATIONS) {
114
+ throw new Error(`PBKDF2 iterations must be at least ${MIN_PBKDF2_ITERATIONS}, got ${count}`);
115
+ }
116
+ return count;
117
+ }
33
118
  /**
34
119
  * Derive a CryptoKey from a password using PBKDF2
35
120
  */
36
- async function deriveKey(password) {
121
+ async function deriveKey(password, iterations) {
37
122
  const encoder = new TextEncoder();
38
123
  const passwordBytes = encoder.encode(password);
39
124
  // Import password as key material
@@ -44,7 +129,7 @@ async function deriveKey(password) {
44
129
  return crypto.subtle.deriveKey({
45
130
  name: 'PBKDF2',
46
131
  salt: PBKDF2_SALT,
47
- iterations: PBKDF2_ITERATIONS,
132
+ iterations,
48
133
  hash: 'SHA-256',
49
134
  }, keyMaterial, { name: 'AES-GCM', length: 256 }, false, ['encrypt']);
50
135
  }
@@ -71,10 +156,17 @@ function generateIV() {
71
156
  * ```typescript
72
157
  * import { encryptSpecimen } from '@wemush/wols/crypto';
73
158
  *
159
+ * // Basic encryption with default iterations (100k)
74
160
  * const encrypted = await encryptSpecimen(specimen, {
75
161
  * key: 'my-secret-password',
76
162
  * });
77
- * // Returns: { encrypted: true, payload: "...", iv: "...", algorithm: "AES-256-GCM" }
163
+ *
164
+ * // Higher security with custom iterations
165
+ * const secureEncrypted = await encryptSpecimen(specimen, {
166
+ * key: 'my-secret-password',
167
+ * iterations: 300_000,
168
+ * });
169
+ * // Returns: { encrypted: true, payload: "...", iv: "...", algorithm: "AES-256-GCM", cryptoVersion: "v1", iterations: 300000 }
78
170
  * ```
79
171
  */
80
172
  export async function encryptSpecimen(specimen, options) {
@@ -83,8 +175,12 @@ export async function encryptSpecimen(specimen, options) {
83
175
  if (fields !== undefined && fields.length > 0) {
84
176
  return encryptPartial(specimen, options);
85
177
  }
178
+ // Validate and get iteration count
179
+ const iterations = validateIterations(options.iterations);
86
180
  // Get or derive the encryption key
87
- const cryptoKey = typeof key === 'string' ? await deriveKey(key) : key;
181
+ // When key is a CryptoKey, iterations is still recorded for documentation
182
+ // but not used (key is already derived)
183
+ const cryptoKey = typeof key === 'string' ? await deriveKey(key, iterations) : key;
88
184
  // Serialize the specimen to JSON
89
185
  const plaintext = serializeSpecimen(specimen);
90
186
  const plaintextBytes = new TextEncoder().encode(plaintext);
@@ -92,12 +188,14 @@ export async function encryptSpecimen(specimen, options) {
92
188
  const { iv, ivBuffer } = generateIV();
93
189
  // Encrypt
94
190
  const ciphertext = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, cryptoKey, plaintextBytes);
95
- // Return encrypted specimen
191
+ // Return encrypted specimen with version and iterations for self-describing decryption
96
192
  return {
97
193
  encrypted: true,
98
194
  payload: toBase64Url(ciphertext),
99
195
  iv: toBase64Url(ivBuffer),
100
196
  algorithm: 'AES-256-GCM',
197
+ cryptoVersion: CRYPTO_VERSION,
198
+ iterations,
101
199
  };
102
200
  }
103
201
  /**
@@ -107,8 +205,10 @@ export async function encryptSpecimen(specimen, options) {
107
205
  */
108
206
  async function encryptPartial(specimen, options) {
109
207
  const { key, fields = [] } = options;
208
+ // Validate and get iteration count
209
+ const iterations = validateIterations(options.iterations);
110
210
  // Get or derive the encryption key
111
- const cryptoKey = typeof key === 'string' ? await deriveKey(key) : key;
211
+ const cryptoKey = typeof key === 'string' ? await deriveKey(key, iterations) : key;
112
212
  // Clone the specimen
113
213
  const result = { ...specimen };
114
214
  // Encrypt each specified field
@@ -124,6 +224,8 @@ async function encryptPartial(specimen, options) {
124
224
  payload: toBase64Url(ciphertext),
125
225
  iv: toBase64Url(ivBuffer),
126
226
  algorithm: 'AES-256-GCM',
227
+ cryptoVersion: CRYPTO_VERSION,
228
+ iterations,
127
229
  };
128
230
  }
129
231
  }
@@ -1 +1 @@
1
- {"version":3,"file":"encrypt.js","sourceRoot":"","sources":["../../../src/crypto/encrypt.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"encrypt.js","sourceRoot":"","sources":["../../../src/crypto/encrypt.ts"],"names":[],"mappings":"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"}
package/dist/esm/index.js CHANGED
@@ -17,6 +17,18 @@ export { WOLS_VERSION, WOLS_CONTEXT } from './types.js';
17
17
  // =============================================================================
18
18
  export { asSpecimenId, isSpecimenType, isGrowthStage, isStrain, SPECIMEN_TYPES, GROWTH_STAGES, } from './types.js';
19
19
  // =============================================================================
20
+ // Type Alias System (v1.2.0)
21
+ // =============================================================================
22
+ export { registerTypeAlias, resolveTypeAlias, getTypeAliases } from './types.js';
23
+ // =============================================================================
24
+ // Type Mapping System (v1.2.0)
25
+ // =============================================================================
26
+ export { WOLS_TO_PLATFORM_MAP, mapToWolsType, mapFromWolsType, registerPlatformType, } from './types.js';
27
+ // =============================================================================
28
+ // Generation Format System (v1.2.0)
29
+ // =============================================================================
30
+ export { normalizeGeneration, isValidGeneration } from './types.js';
31
+ // =============================================================================
20
32
  // Errors
21
33
  // =============================================================================
22
34
  export { WolsErrorCode, WolsError, WolsParseError, WolsValidationError, WolsEncryptionError, } from './errors.js';
@@ -35,7 +47,7 @@ export { parseSpecimen } from './parser.js';
35
47
  // =============================================================================
36
48
  // Core Functions - Compact URL
37
49
  // =============================================================================
38
- export { toCompactUrl, parseCompactUrl, SPECIES_CODES, getSpeciesFromCode, getCodeFromSpecies, registerSpeciesCode, } from './compact-url.js';
50
+ export { toCompactUrl, parseCompactUrl, parseCompactUrlOrThrow, parseCompactUrlOrNull, SPECIES_CODES, getSpeciesFromCode, getCodeFromSpecies, registerSpeciesCode, } from './compact-url.js';
39
51
  // =============================================================================
40
52
  // Core Functions - Encryption
41
53
  // =============================================================================
@@ -44,4 +56,12 @@ export { encryptSpecimen, isEncrypted, decryptSpecimen } from './crypto/index.js
44
56
  // Core Functions - QR Code Generation
45
57
  // =============================================================================
46
58
  export { toQRCode, toQRCodeDataURL, toQRCodeSVG, isQRAvailable, isQRAvailableSync, } from './qr/index.js';
59
+ // =============================================================================
60
+ // Environment Detection (v1.2.0)
61
+ // =============================================================================
62
+ export { isServer, isBrowser, isWebWorker, isDeno, isBun, getRuntimeEnvironment, supportsWebCrypto, supportsNodeCrypto, isCryptoSupported, supportsTextEncoding, supportsURLAPIs, } from './utils/environment.js';
63
+ // =============================================================================
64
+ // Migration Utilities (v1.2.0)
65
+ // =============================================================================
66
+ export { compareVersions, isOutdated, isNewer, getCurrentVersion, registerMigration, canMigrate, migrate, getMigrations, clearMigrations, } from './utils/migration.js';
47
67
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,gFAAgF;AAChF,YAAY;AACZ,gFAAgF;AAChF,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAoCxD,gFAAgF;AAChF,iBAAiB;AACjB,gFAAgF;AAChF,OAAO,EACL,YAAY,EACZ,cAAc,EACd,aAAa,EACb,QAAQ,EACR,cAAc,EACd,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,gFAAgF;AAChF,SAAS;AACT,gFAAgF;AAChF,OAAO,EACL,aAAa,EACb,SAAS,EACT,cAAc,EACd,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,gFAAgF;AAChF,4BAA4B;AAC5B,gFAAgF;AAChF,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElE,gFAAgF;AAChF,8BAA8B;AAC9B,gFAAgF;AAChF,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,gFAAgF;AAChF,0BAA0B;AAC1B,gFAAgF;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,gFAAgF;AAChF,+BAA+B;AAC/B,gFAAgF;AAChF,OAAO,EACL,YAAY,EACZ,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAE1B,gFAAgF;AAChF,8BAA8B;AAC9B,gFAAgF;AAChF,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAElF,gFAAgF;AAChF,sCAAsC;AACtC,gFAAgF;AAChF,OAAO,EACL,QAAQ,EACR,eAAe,EACf,WAAW,EACX,aAAa,EACb,iBAAiB,GAClB,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,gFAAgF;AAChF,YAAY;AACZ,gFAAgF;AAChF,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAyCxD,gFAAgF;AAChF,iBAAiB;AACjB,gFAAgF;AAChF,OAAO,EACL,YAAY,EACZ,cAAc,EACd,aAAa,EACb,QAAQ,EACR,cAAc,EACd,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,gFAAgF;AAChF,6BAA6B;AAC7B,gFAAgF;AAChF,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjF,gFAAgF;AAChF,+BAA+B;AAC/B,gFAAgF;AAChF,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,eAAe,EACf,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAEpB,gFAAgF;AAChF,oCAAoC;AACpC,gFAAgF;AAChF,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAGpE,gFAAgF;AAChF,SAAS;AACT,gFAAgF;AAChF,OAAO,EACL,aAAa,EACb,SAAS,EACT,cAAc,EACd,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,gFAAgF;AAChF,4BAA4B;AAC5B,gFAAgF;AAChF,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElE,gFAAgF;AAChF,8BAA8B;AAC9B,gFAAgF;AAChF,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,gFAAgF;AAChF,0BAA0B;AAC1B,gFAAgF;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,gFAAgF;AAChF,+BAA+B;AAC/B,gFAAgF;AAChF,OAAO,EACL,YAAY,EACZ,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAE1B,gFAAgF;AAChF,8BAA8B;AAC9B,gFAAgF;AAChF,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAElF,gFAAgF;AAChF,sCAAsC;AACtC,gFAAgF;AAChF,OAAO,EACL,QAAQ,EACR,eAAe,EACf,WAAW,EACX,aAAa,EACb,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAEvB,gFAAgF;AAChF,iCAAiC;AACjC,gFAAgF;AAChF,OAAO,EACL,QAAQ,EACR,SAAS,EACT,WAAW,EACX,MAAM,EACN,KAAK,EACL,qBAAqB,EACrB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,GAChB,MAAM,wBAAwB,CAAC;AAGhC,gFAAgF;AAChF,+BAA+B;AAC/B,gFAAgF;AAChF,OAAO,EACL,eAAe,EACf,UAAU,EACV,OAAO,EACP,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EACV,OAAO,EACP,aAAa,EACb,eAAe,GAChB,MAAM,sBAAsB,CAAC"}
@@ -137,6 +137,9 @@ export function parseSpecimen(json) {
137
137
  if (data['signature'] !== undefined) {
138
138
  specimen.signature = data['signature'];
139
139
  }
140
+ if (data['_meta'] !== undefined) {
141
+ specimen._meta = data['_meta'];
142
+ }
140
143
  return {
141
144
  success: true,
142
145
  data: specimen,
@@ -1 +1 @@
1
- {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/parser.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/parser.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * @module specimen
8
8
  */
9
- import { WOLS_CONTEXT, WOLS_VERSION, asSpecimenId, isStrain } from './types.js';
9
+ import { WOLS_CONTEXT, WOLS_VERSION, asSpecimenId, isSpecimenType, isStrain, resolveTypeAlias, } from './types.js';
10
10
  import { createSpecimenId } from './utils/cuid.js';
11
11
  /**
12
12
  * Expand a strain input to a full Strain object.
@@ -69,13 +69,20 @@ function expandStrain(strain) {
69
69
  export function createSpecimen(input) {
70
70
  const id = asSpecimenId(createSpecimenId());
71
71
  const strain = expandStrain(input.strain);
72
+ // Resolve type alias to canonical WOLS type (e.g., 'LIQUID_CULTURE' -> 'CULTURE')
73
+ const resolvedType = resolveTypeAlias(input.type);
74
+ if (!isSpecimenType(resolvedType)) {
75
+ throw new Error(`Invalid specimen type: '${input.type}'. ` +
76
+ `Must be one of: CULTURE, SPAWN, SUBSTRATE, FRUITING, HARVEST ` +
77
+ `(or a registered alias).`);
78
+ }
72
79
  // Build specimen with required fields
73
80
  const specimen = {
74
81
  '@context': WOLS_CONTEXT,
75
82
  '@type': 'Specimen',
76
83
  id,
77
84
  version: WOLS_VERSION,
78
- type: input.type,
85
+ type: resolvedType,
79
86
  species: input.species,
80
87
  };
81
88
  // Add optional fields only if defined
@@ -97,6 +104,9 @@ export function createSpecimen(input) {
97
104
  if (input.custom !== undefined) {
98
105
  specimen.custom = input.custom;
99
106
  }
107
+ if (input._meta !== undefined) {
108
+ specimen._meta = input._meta;
109
+ }
100
110
  return specimen;
101
111
  }
102
112
  /**
@@ -154,6 +164,9 @@ export function serializeSpecimen(specimen) {
154
164
  if (specimen.signature !== undefined) {
155
165
  ordered['signature'] = specimen.signature;
156
166
  }
167
+ if (specimen._meta !== undefined) {
168
+ ordered['_meta'] = specimen._meta;
169
+ }
157
170
  return JSON.stringify(ordered);
158
171
  }
159
172
  //# sourceMappingURL=specimen.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"specimen.js","sourceRoot":"","sources":["../../src/specimen.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"specimen.js","sourceRoot":"","sources":["../../src/specimen.ts"],"names":[],"mappings":"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"}
package/dist/esm/types.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * WOLS TypeScript Library - Core Types
3
3
  *
4
4
  * This module defines all the core types, interfaces, and enums
5
- * for the WeMush Open Labeling Standard (WOLS) v1.1.0.
5
+ * for the WeMush Open Labeling Standard (WOLS) v1.2.0.
6
6
  *
7
7
  * @packageDocumentation
8
8
  * @module @wemush/wols
@@ -13,7 +13,7 @@
13
13
  /**
14
14
  * WOLS specification version implemented by this library.
15
15
  */
16
- export const WOLS_VERSION = '1.1.0';
16
+ export const WOLS_VERSION = '1.2.0';
17
17
  /**
18
18
  * JSON-LD context URL for WOLS specimens.
19
19
  */
@@ -35,15 +35,271 @@ export const SPECIMEN_TYPES = [
35
35
  'FRUITING',
36
36
  'HARVEST',
37
37
  ];
38
+ // =============================================================================
39
+ // TYPE ALIAS SYSTEM (v1.2.0)
40
+ // =============================================================================
41
+ /**
42
+ * Type alias registry for mapping platform-specific types to WOLS types.
43
+ * @internal
44
+ */
45
+ const TYPE_ALIAS_REGISTRY = new Map([
46
+ // Culture aliases
47
+ ['LIQUID_CULTURE', 'CULTURE'],
48
+ ['LC', 'CULTURE'],
49
+ ['AGAR', 'CULTURE'],
50
+ ['PETRI', 'CULTURE'],
51
+ ['SLANT', 'CULTURE'],
52
+ ['CULTURE_PLATE', 'CULTURE'],
53
+ // Spawn aliases
54
+ ['GRAIN_SPAWN', 'SPAWN'],
55
+ ['SAWDUST_SPAWN', 'SPAWN'],
56
+ ['PLUG_SPAWN', 'SPAWN'],
57
+ ['DOWEL', 'SPAWN'],
58
+ // Substrate aliases
59
+ ['BLOCK', 'SUBSTRATE'],
60
+ ['BAG', 'SUBSTRATE'],
61
+ ['BED', 'SUBSTRATE'],
62
+ ['LOG', 'SUBSTRATE'],
63
+ ['BUCKET', 'SUBSTRATE'],
64
+ // Fruiting aliases
65
+ ['FLUSH', 'FRUITING'],
66
+ ['PINNING', 'FRUITING'],
67
+ ['PRIMORDIA', 'FRUITING'], // Scientifically precise term for pin initiation stage
68
+ ['FRUIT', 'FRUITING'],
69
+ ['FIRST_FLUSH', 'FRUITING'],
70
+ // Harvest aliases
71
+ ['FRESH', 'HARVEST'],
72
+ ['DRIED', 'HARVEST'],
73
+ ['PROCESSED', 'HARVEST'],
74
+ ['EXTRACT', 'HARVEST'],
75
+ ]);
76
+ /**
77
+ * Register a custom type alias.
78
+ *
79
+ * @param alias - The alias string (case-insensitive, stored uppercase)
80
+ * @param wolsType - The canonical WOLS SpecimenType
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * registerTypeAlias('MYCELIUM_BLOCK', 'SUBSTRATE');
85
+ * ```
86
+ */
87
+ export function registerTypeAlias(alias, wolsType) {
88
+ TYPE_ALIAS_REGISTRY.set(alias.toUpperCase(), wolsType);
89
+ }
90
+ /**
91
+ * Resolve a type alias to its canonical WOLS type.
92
+ * Returns the input if it's already a valid SpecimenType or no alias exists.
93
+ *
94
+ * @param typeOrAlias - Type string or alias to resolve
95
+ * @returns Canonical SpecimenType or original string if not found
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * resolveTypeAlias('LIQUID_CULTURE') // Returns 'CULTURE'
100
+ * resolveTypeAlias('CULTURE') // Returns 'CULTURE'
101
+ * resolveTypeAlias('unknown') // Returns 'unknown'
102
+ * ```
103
+ */
104
+ export function resolveTypeAlias(typeOrAlias) {
105
+ const upper = typeOrAlias.toUpperCase();
106
+ // Already a valid type?
107
+ if (SPECIMEN_TYPES.includes(upper)) {
108
+ return upper;
109
+ }
110
+ // Check alias registry
111
+ const resolved = TYPE_ALIAS_REGISTRY.get(upper);
112
+ return resolved ?? typeOrAlias;
113
+ }
114
+ /**
115
+ * Get all registered type aliases.
116
+ *
117
+ * @returns ReadonlyMap of alias -> SpecimenType mappings
118
+ */
119
+ export function getTypeAliases() {
120
+ return TYPE_ALIAS_REGISTRY;
121
+ }
122
+ // =============================================================================
123
+ // TYPE MAPPING EXPORTS (v1.2.0)
124
+ // =============================================================================
125
+ /**
126
+ * Mapping from WOLS types to common platform type names.
127
+ *
128
+ * This provides a centralized lookup for integrations that need to
129
+ * display user-friendly type names or map to platform-specific values.
130
+ *
131
+ * @since 1.2.0
132
+ */
133
+ export const WOLS_TO_PLATFORM_MAP = {
134
+ CULTURE: ['Liquid Culture', 'LC', 'Agar', 'Petri', 'Slant', 'Culture Plate'],
135
+ SPAWN: ['Grain Spawn', 'Sawdust Spawn', 'Plug Spawn', 'Dowel'],
136
+ SUBSTRATE: ['Block', 'Bag', 'Bed', 'Log', 'Bucket'],
137
+ FRUITING: ['Pinning', 'Primordia', 'Fruiting', 'Flush', 'First Flush'],
138
+ HARVEST: ['Fresh', 'Dried', 'Processed', 'Extract'],
139
+ };
140
+ /**
141
+ * Map a platform-specific type to a WOLS SpecimenType.
142
+ *
143
+ * First checks registered aliases, then falls back to direct lookup.
144
+ *
145
+ * @param platformType - Platform-specific type name
146
+ * @returns Matching SpecimenType or null if not found
147
+ *
148
+ * @example
149
+ * ```typescript
150
+ * mapToWolsType('Liquid Culture') // Returns 'CULTURE'
151
+ * mapToWolsType('LC') // Returns 'CULTURE'
152
+ * mapToWolsType('Unknown Type') // Returns null
153
+ * ```
154
+ *
155
+ * @since 1.2.0
156
+ */
157
+ export function mapToWolsType(platformType) {
158
+ // First try the alias registry
159
+ const resolved = resolveTypeAlias(platformType);
160
+ if (isSpecimenType(resolved)) {
161
+ return resolved;
162
+ }
163
+ // Fall back to searching WOLS_TO_PLATFORM_MAP
164
+ const normalized = platformType.toUpperCase().replace(/\s+/g, '_');
165
+ for (const [wolsType, platformTypes] of Object.entries(WOLS_TO_PLATFORM_MAP)) {
166
+ for (const pt of platformTypes) {
167
+ if (pt.toUpperCase().replace(/\s+/g, '_') === normalized) {
168
+ return wolsType;
169
+ }
170
+ }
171
+ }
172
+ return null;
173
+ }
174
+ /**
175
+ * Get all platform type names for a WOLS SpecimenType.
176
+ *
177
+ * @param wolsType - WOLS SpecimenType
178
+ * @returns Array of platform type names
179
+ *
180
+ * @example
181
+ * ```typescript
182
+ * mapFromWolsType('CULTURE')
183
+ * // Returns ['Liquid Culture', 'LC', 'Agar', 'Petri', 'Slant', 'Culture Plate']
184
+ * ```
185
+ *
186
+ * @since 1.2.0
187
+ */
188
+ export function mapFromWolsType(wolsType) {
189
+ return WOLS_TO_PLATFORM_MAP[wolsType] ?? [];
190
+ }
191
+ /**
192
+ * Register a custom platform type mapping.
193
+ *
194
+ * This adds to both the alias registry (for resolveTypeAlias) and
195
+ * can be used for custom platform integrations.
196
+ *
197
+ * @param platformType - Platform-specific type name
198
+ * @param wolsType - WOLS SpecimenType to map to
199
+ *
200
+ * @example
201
+ * ```typescript
202
+ * registerPlatformType('Mycelium Block', 'SUBSTRATE');
203
+ * mapToWolsType('Mycelium Block') // Returns 'SUBSTRATE'
204
+ * ```
205
+ *
206
+ * @since 1.2.0
207
+ */
208
+ export function registerPlatformType(platformType, wolsType) {
209
+ // Add to alias registry for resolveTypeAlias support
210
+ registerTypeAlias(platformType, wolsType);
211
+ }
38
212
  /**
39
213
  * Array of valid growth stages for validation.
214
+ * Ordered by typical lifecycle progression.
40
215
  */
41
216
  export const GROWTH_STAGES = [
42
217
  'INOCULATION',
218
+ 'INCUBATION',
43
219
  'COLONIZATION',
220
+ 'PRIMORDIA',
44
221
  'FRUITING',
222
+ 'SENESCENCE',
45
223
  'HARVEST',
46
224
  ];
225
+ /**
226
+ * Pattern for parsing generation strings.
227
+ * Matches: P, P1, F{n}, G{n}, or plain numbers.
228
+ */
229
+ const GENERATION_PARSE_PATTERN = /^(P|P1|F(\d+)|G(\d+)|(\d+))$/i;
230
+ /**
231
+ * Normalize a generation string to the specified format.
232
+ *
233
+ * Accepts multiple input formats:
234
+ * - Filial: F1, F2, F3, etc.
235
+ * - Parental: P or P1
236
+ * - Generational: G1, G2, G3, etc.
237
+ * - Numeric: 1, 2, 3, etc.
238
+ *
239
+ * @param generation - The generation string to normalize
240
+ * @param format - Target format (default: 'preserve')
241
+ * @returns Normalized generation string, or original if not parseable
242
+ *
243
+ * @example
244
+ * ```typescript
245
+ * normalizeGeneration('G2', 'filial') // Returns 'F2'
246
+ * normalizeGeneration('3', 'filial') // Returns 'F3'
247
+ * normalizeGeneration('F1', 'numeric') // Returns '1'
248
+ * normalizeGeneration('P', 'filial') // Returns 'P'
249
+ * ```
250
+ *
251
+ * @since 1.2.0
252
+ */
253
+ export function normalizeGeneration(generation, format = 'preserve') {
254
+ const trimmed = generation.trim().toUpperCase();
255
+ // Handle parental generation specially
256
+ if (trimmed === 'P' || trimmed === 'P1') {
257
+ return format === 'numeric' ? '0' : 'P';
258
+ }
259
+ const match = GENERATION_PARSE_PATTERN.exec(trimmed);
260
+ if (!match) {
261
+ return generation; // Return original if not parseable
262
+ }
263
+ // Extract the numeric value
264
+ let numericValue;
265
+ if (match[2] !== undefined) {
266
+ // F{n} format
267
+ numericValue = parseInt(match[2], 10);
268
+ }
269
+ else if (match[3] !== undefined) {
270
+ // G{n} format
271
+ numericValue = parseInt(match[3], 10);
272
+ }
273
+ else if (match[4] !== undefined) {
274
+ // Plain number
275
+ numericValue = parseInt(match[4], 10);
276
+ }
277
+ else {
278
+ return generation; // Shouldn't reach here
279
+ }
280
+ switch (format) {
281
+ case 'preserve':
282
+ return trimmed;
283
+ case 'filial':
284
+ return `F${numericValue}`;
285
+ case 'numeric':
286
+ return String(numericValue);
287
+ default:
288
+ return generation;
289
+ }
290
+ }
291
+ /**
292
+ * Check if a generation string is valid.
293
+ *
294
+ * @param generation - The generation string to validate
295
+ * @returns True if valid, false otherwise
296
+ *
297
+ * @since 1.2.0
298
+ */
299
+ export function isValidGeneration(generation) {
300
+ const trimmed = generation.trim().toUpperCase();
301
+ return GENERATION_PARSE_PATTERN.test(trimmed);
302
+ }
47
303
  // =============================================================================
48
304
  // TYPE GUARDS
49
305
  // =============================================================================