@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
@@ -1,87 +1,132 @@
1
1
  /**
2
- * WOLS Encryption Module
2
+ * WOLS Encryption Module (WOLS v1.3.0 encrypted format)
3
3
  *
4
- * Provides AES-256-GCM encryption for specimens using Web Crypto API.
5
- * Supports both password-based (PBKDF2) and raw CryptoKey encryption.
4
+ * AES-256-GCM via WebCrypto, so it runs in Node.js 20+, browsers, Deno and Bun.
6
5
  *
7
- * ## Security Model
6
+ * ## Wire format
8
7
  *
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)
8
+ * ```
9
+ * wemush://v1/encrypted/{id}?e={ciphertext}&iv={iv}&kdf={pbkdf2|raw}[&salt={salt}&i={iterations}]
10
+ * ```
13
11
  *
14
- * ### Password vs CryptoKey
12
+ * - `e`: ciphertext with the 16-byte GCM tag appended
13
+ * - `iv`: fresh random 12-byte IV per encryption
14
+ * - All binary values are base64url without padding
15
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) |
16
+ * ## Keys
20
17
  *
21
- * ### Security Considerations
18
+ * | `key` value | `kdf` | Notes |
19
+ * |-------------|-------|-------|
20
+ * | `string` passphrase | `pbkdf2` | PBKDF2-HMAC-SHA256, fresh random 16-byte salt per label, ≥100,000 iterations |
21
+ * | 32-byte `Uint8Array` / `ArrayBuffer` | `raw` | Key shared out of band |
22
+ * | AES-GCM 256 `CryptoKey` | `raw` | Key shared out of band |
22
23
  *
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.
24
+ * Pre-1.3.0 releases derived passphrase keys with a fixed library-wide salt.
25
+ * That scheme is never generated any more; see `allowLegacy` on decryption.
26
+ *
27
+ * @module crypto/encrypt
28
+ */
29
+ import type { EncryptedField, EncryptedSpecimen, EncryptionOptions, FieldEncryptedSpecimen, FieldEncryptedSpecimenOf, FieldEncryptionOptions, Specimen } from '../types.js';
30
+ /**
31
+ * Build the encrypted-format URL for an encrypted specimen.
32
+ *
33
+ * Enforces the generator rules, so a hand-built object cannot produce a
34
+ * non-conformant label:
35
+ * - `payload` and `iv` are canonical unpadded base64url, the IV is 12 bytes,
36
+ * and the payload is at least the 16-byte GCM tag
37
+ * - `kdf` is present: `pbkdf2` needs 100,000 to 2^32-1 iterations and a salt
38
+ * of at least 16 bytes that is not the legacy library salt (compared as
39
+ * bytes); `raw` must not carry `salt` or `iterations`
40
+ *
41
+ * Labels with more than 10,000,000 iterations are valid but exceed the
42
+ * decoders' default `maxIterations`, so readers must raise it.
43
+ *
44
+ * @throws WolsEncryptionError when the object would produce a non-conformant
45
+ * label (including legacy payloads, which are never generated)
46
+ * @since 1.3.0
47
+ */
48
+ export declare function toEncryptedUrl(encrypted: EncryptedSpecimen): string;
49
+ /**
50
+ * Encrypt a whole specimen with AES-256-GCM (WOLS v1.3.0 encrypted format).
25
51
  *
26
- * 2. **Iteration Count**: Default 100,000 iterations. Can be increased via `iterations`
27
- * option for higher security (at cost of performance).
52
+ * The specimen's embedded JSON-LD serialization is encrypted and the result
53
+ * carries `url`, the `wemush://v1/encrypted/...` string to encode in a QR code.
54
+ * For field-level encryption use {@link encryptFields}.
28
55
  *
29
- * 3. **Random IV**: Each encryption uses a cryptographically random 96-bit IV,
30
- * ensuring identical plaintexts produce different ciphertexts.
56
+ * @param specimen - The specimen to encrypt
57
+ * @param options - `key` (passphrase or raw key) and optional `iterations`
58
+ * @throws WolsEncryptionError for an unusable key, too few iterations, an id
59
+ * without a suffix, or a runtime without WebCrypto
31
60
  *
32
- * 4. **Authenticated Encryption**: AES-256-GCM provides both confidentiality and
33
- * integrity - tampered ciphertext will fail decryption.
61
+ * @example
62
+ * ```typescript
63
+ * // Passphrase: kdf=pbkdf2 with a random per-label salt
64
+ * const enc = await encryptSpecimen(specimen, { key: 'correct horse battery staple' });
65
+ * enc.url; // 'wemush://v1/encrypted/clx1a2b3c4?e=...&iv=...&kdf=pbkdf2&salt=...&i=100000'
34
66
  *
35
- * ## Future Specification Updates (v1.2.1+)
67
+ * // Raw 32-byte key: kdf=raw
68
+ * const raw = await encryptSpecimen(specimen, { key: crypto.getRandomValues(new Uint8Array(32)) });
69
+ * ```
36
70
  *
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:
71
+ * @since 1.3.0 Per-label random salt, `kdf`, `id` and `url`; raw key bytes accepted.
72
+ * The `fields` option is deprecated in favor of `encryptFields()`.
73
+ */
74
+ export declare function encryptSpecimen(specimen: Specimen, options: EncryptionOptions & {
75
+ fields: readonly string[];
76
+ }): Promise<FieldEncryptedSpecimen>;
77
+ export declare function encryptSpecimen(specimen: Specimen, options: EncryptionOptions): Promise<EncryptedSpecimen>;
78
+ /**
79
+ * Field-level encryption (WOLS v1.3.0): replace the value of each named field
80
+ * with an {@link EncryptedField} and leave the rest of the specimen in cleartext.
39
81
  *
40
- * 1. **EncryptedSpecimen schema**: Define the `cryptoVersion` and `iterations` fields
41
- * in the encrypted payload format section.
82
+ * One key (and, for passphrases, one random salt) per call; a fresh IV per field.
83
+ * Each value is bound to its label and field with AES-GCM additional data:
84
+ * the UTF-8 of `"{id}\n{field}"` (WOLS 1.3.0). A value moved to another field
85
+ * or copied into another label fails authentication. Restore with `decryptFields()`.
42
86
  *
43
- * 2. **Configurable iterations**: Document the `iterations` option in EncryptionOptions
44
- * with recommended ranges (100k-600k) and minimum floor (10k).
87
+ * The identifying fields (`@context`, `@type`, `id`, `version`, `type`,
88
+ * `species`) can't be encrypted.
45
89
  *
46
- * 3. **Self-describing payloads**: Explain that v1.2.0+ payloads include derivation
47
- * parameters for forward-compatible decryption.
90
+ * @param specimen - The specimen to partially encrypt
91
+ * @param options - `key`, `fields` (each must be present on the specimen), optional `iterations`
92
+ * @throws WolsEncryptionError when `fields` is empty, names a field the specimen
93
+ * does not have, or names a field twice; or for an unusable key
48
94
  *
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)
95
+ * @example
96
+ * ```typescript
97
+ * const partial = await encryptFields(specimen, { key: passphrase, fields: ['strain'] });
98
+ * partial.species; // still cleartext
99
+ * partial.strain; // { encrypted: true, algorithm: 'AES-256-GCM', payload, iv, kdf: 'pbkdf2', salt, iterations }
100
+ * ```
54
101
  *
55
- * @module crypto/encrypt
102
+ * @since 1.3.0
56
103
  */
57
- import type { Specimen, EncryptedSpecimen, EncryptionOptions } from '../types.js';
104
+ export declare function encryptFields<K extends keyof Specimen>(specimen: Specimen, options: FieldEncryptionOptions<K>): Promise<FieldEncryptedSpecimenOf<K>>;
58
105
  /**
59
- * Encrypt a specimen using AES-256-GCM
106
+ * Whether a value is a field-level {@link EncryptedField}: an encrypted value
107
+ * with a `kdf` and no specimen `id` or `url`.
60
108
  *
61
- * @param specimen - The specimen to encrypt
62
- * @param options - Encryption options (key required)
63
- * @returns Promise resolving to encrypted specimen wrapper
109
+ * The 1.3.0 shapes are told apart structurally: whole-label objects produced
110
+ * by `encryptSpecimen` and `parseEncryptedUrl` always carry `id`, and field
111
+ * values never do. Legacy (pre-1.3.0) values have no `kdf` and are reported
112
+ * as {@link isEncryptedSpecimen}, whether they were whole labels or fields.
113
+ * `decryptFields` handles legacy field values regardless (with `allowLegacy`).
64
114
  *
65
- * @example
66
- * ```typescript
67
- * import { encryptSpecimen } from '@wemush/wols/crypto';
68
- *
69
- * // Basic encryption with default iterations (100k)
70
- * const encrypted = await encryptSpecimen(specimen, {
71
- * key: 'my-secret-password',
72
- * });
73
- *
74
- * // Higher security with custom iterations
75
- * const secureEncrypted = await encryptSpecimen(specimen, {
76
- * key: 'my-secret-password',
77
- * iterations: 300_000,
78
- * });
79
- * // Returns: { encrypted: true, payload: "...", iv: "...", algorithm: "AES-256-GCM", cryptoVersion: "v1", iterations: 300000 }
80
- * ```
115
+ * @since 1.3.0
81
116
  */
82
- export declare function encryptSpecimen(specimen: Specimen, options: EncryptionOptions): Promise<EncryptedSpecimen>;
117
+ export declare function isEncryptedField(value: unknown): value is EncryptedField;
83
118
  /**
84
- * Type guard to check if an object is an encrypted specimen
119
+ * Whether a value is a whole-label {@link EncryptedSpecimen} (v1.3.0 or legacy).
120
+ *
121
+ * @since 1.3.0
122
+ */
123
+ export declare function isEncryptedSpecimen(value: unknown): value is EncryptedSpecimen;
124
+ /**
125
+ * Whether a value carries the `encrypted: true` marker: either a whole-label
126
+ * {@link EncryptedSpecimen} or a field-level {@link EncryptedField}. Use
127
+ * {@link isEncryptedSpecimen} or {@link isEncryptedField} to tell them apart.
128
+ *
129
+ * @since 1.3.0 Narrows to `EncryptedSpecimen | EncryptedField`
85
130
  */
86
- export declare function isEncrypted(value: unknown): value is EncryptedSpecimen;
131
+ export declare function isEncrypted(value: unknown): value is EncryptedSpecimen | EncryptedField;
87
132
  //# sourceMappingURL=encrypt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"encrypt.d.ts","sourceRoot":"","sources":["../../../src/crypto/encrypt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAkHlF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,iBAAiB,CAAC,CAuC5B;AAoDD;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAStE"}
1
+ {"version":3,"file":"encrypt.d.ts","sourceRoot":"","sources":["../../../src/crypto/encrypt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAIH,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EAEjB,iBAAiB,EACjB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,QAAQ,EACT,MAAM,aAAa,CAAC;AA+IrB;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,iBAAiB,GAAG,MAAM,CAmCnE;AAMD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,iBAAiB,GAAG;IAAE,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GACzD,OAAO,CAAC,sBAAsB,CAAC,CAAC;AACnC,wBAAsB,eAAe,CACnC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAuC9B;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,aAAa,CAAC,CAAC,SAAS,MAAM,QAAQ,EAC1D,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,sBAAsB,CAAC,CAAC,CAAC,GACjC,OAAO,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAuDtC;AAgBD;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAOxE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAE9E;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,GAAG,cAAc,CAKvF"}
@@ -1,10 +1,12 @@
1
1
  /**
2
2
  * WOLS Crypto Module
3
3
  *
4
- * Provides AES-256-GCM encryption and decryption for specimens.
4
+ * AES-256-GCM encryption and decryption for specimens in the WOLS v1.3.0
5
+ * encrypted format (`wemush://v1/encrypted/{id}?e=&iv=&kdf=...`).
5
6
  *
6
7
  * @module crypto
7
8
  */
8
- export { encryptSpecimen, isEncrypted } from './encrypt.js';
9
- export { decryptSpecimen } from './decrypt.js';
9
+ export { encryptSpecimen, encryptFields, isEncrypted, isEncryptedSpecimen, isEncryptedField, toEncryptedUrl, } from './encrypt.js';
10
+ export { decryptSpecimen, decryptPlaintext, decryptField, decryptFields, parseEncryptedUrl, type FieldBinding, } from './decrypt.js';
11
+ export { base64UrlEncode, base64UrlDecode, derivePassphraseKey } from './shared.js';
10
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/crypto/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/crypto/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,eAAe,EACf,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Shared primitives for the WOLS encrypted format (v1.3.0).
3
+ *
4
+ * Uses only WebCrypto (`globalThis.crypto.subtle`), so it runs unchanged in
5
+ * Node.js 20+, browsers, Deno, Bun and workers.
6
+ *
7
+ * `base64UrlEncode`, `base64UrlDecode` and `derivePassphraseKey` are public
8
+ * (re-exported from the package root and `@wemush/wols/crypto`); everything
9
+ * else here is internal to the crypto module.
10
+ *
11
+ * @module crypto/shared
12
+ */
13
+ import { WolsDecryptionError } from '../errors.js';
14
+ import type { DecryptionFailureReason, RawEncryptionKey } from '../types.js';
15
+ /** Default PBKDF2 iteration count used when generating. */
16
+ export declare const DEFAULT_PBKDF2_ITERATIONS = 600000;
17
+ /** Iteration count pre-1.3.0 payloads used when they recorded none. */
18
+ export declare const LEGACY_PBKDF2_ITERATIONS = 100000;
19
+ /** Generators MUST use at least this many PBKDF2 iterations (WOLS v1.3.0). */
20
+ export declare const MIN_GENERATE_ITERATIONS = 100000;
21
+ /** Decoders MUST reject fewer PBKDF2 iterations than this (WOLS v1.3.0). */
22
+ export declare const MIN_DECODE_ITERATIONS = 10000;
23
+ /** Default largest PBKDF2 iteration count accepted from a label when decoding. */
24
+ export declare const DEFAULT_MAX_DECODE_ITERATIONS = 10000000;
25
+ /**
26
+ * WOLS 1.3.0 ceiling: decoders reject more than this many iterations (alone,
27
+ * or in total across a field-encrypted label), and generators never exceed it.
28
+ * A configured `maxIterations` may be lower, never higher.
29
+ */
30
+ export declare const MAX_ITERATIONS = 10000000;
31
+ /** WebCrypto's PBKDF2 `iterations` is an unsigned long; nothing above this is derivable. */
32
+ export declare const MAX_PBKDF2_ITERATIONS = 4294967295;
33
+ /** Per-label PBKDF2 salt length generated (spec minimum: 16 bytes). */
34
+ export declare const SALT_LENGTH = 16;
35
+ /** AES-GCM appends a 16-byte authentication tag, so no valid ciphertext is shorter. */
36
+ export declare const GCM_TAG_LENGTH = 16;
37
+ /** AES-GCM IV length in bytes. */
38
+ export declare const IV_LENGTH = 12;
39
+ /** Raw AES-256 key length in bytes. */
40
+ export declare const RAW_KEY_LENGTH = 32;
41
+ /** Prefix of every encrypted-format URL. */
42
+ export declare const ENCRYPTED_URL_PREFIX = "wemush://v1/encrypted/";
43
+ /**
44
+ * Library-wide salt used by pre-1.3.0 releases. Only for decoding legacy
45
+ * payloads behind `allowLegacy`; never used when generating.
46
+ */
47
+ export declare const LEGACY_PBKDF2_SALT: Uint8Array<ArrayBuffer>;
48
+ /**
49
+ * Create a decryption error carrying a stable `details.reason` category.
50
+ */
51
+ export declare function decryptionError(reason: DecryptionFailureReason, message: string, details?: Record<string, unknown>): WolsDecryptionError;
52
+ /**
53
+ * Whether WebCrypto (`globalThis.crypto.subtle`) is available. It is global in
54
+ * browsers, workers, Deno, Bun and Node.js 19+ (Node 18 needs
55
+ * `--experimental-global-webcrypto`).
56
+ */
57
+ export declare function hasWebCrypto(): boolean;
58
+ /** Message used when WebCrypto is missing. */
59
+ export declare const NO_WEBCRYPTO_MESSAGE: string;
60
+ /** Throw a WolsEncryptionError when WebCrypto is missing (encryption paths). */
61
+ export declare function assertWebCrypto(): void;
62
+ /**
63
+ * Encode bytes as base64url (RFC 4648 §5) without padding.
64
+ *
65
+ * @since 1.3.0
66
+ */
67
+ export declare function base64UrlEncode(bytes: Uint8Array | ArrayBuffer): string;
68
+ /**
69
+ * Decode base64url (RFC 4648 §5). Padded input is accepted.
70
+ *
71
+ * @throws Error if the input is not valid base64url
72
+ * @since 1.3.0
73
+ */
74
+ export declare function base64UrlDecode(value: string): Uint8Array<ArrayBuffer>;
75
+ /** Cryptographically random bytes from WebCrypto. */
76
+ export declare function randomBytes(length: number): Uint8Array<ArrayBuffer>;
77
+ /**
78
+ * Derive a 256-bit key from a passphrase with PBKDF2-HMAC-SHA256.
79
+ *
80
+ * Exposed for debugging and interoperability testing (it reproduces the
81
+ * conformance `derive` vectors). Returns the raw key bytes.
82
+ *
83
+ * @param passphrase - Passphrase; normalized to Unicode NFC, then UTF-8 encoded (WOLS 1.3.0)
84
+ * @param salt - Salt bytes (the label's decoded `salt` parameter)
85
+ * @param iterations - PBKDF2 iteration count (positive integer)
86
+ * @since 1.3.0
87
+ */
88
+ export declare function derivePassphraseKey(passphrase: string, salt: Uint8Array | ArrayBuffer, iterations: number): Promise<Uint8Array<ArrayBuffer>>;
89
+ /**
90
+ * Field-level AES-GCM additional data (WOLS 1.3.0): UTF-8 of the label's full
91
+ * `id`, a line feed, and the top-level field name, e.g. "wemush:clx1a2b3c4\nstrain".
92
+ * Binds each encrypted value to its label and field.
93
+ */
94
+ export declare function fieldAad(id: string, field: string): Uint8Array<ArrayBuffer>;
95
+ /**
96
+ * Fields that identify or type a label. They are never encrypted, and a label
97
+ * in which any of them is an encrypted object is refused (WOLS 1.3.0).
98
+ */
99
+ export declare const IDENTIFYING_FIELDS: readonly string[];
100
+ /** Import 32 raw key bytes as an AES-GCM key. */
101
+ export declare function importAesKey(bytes: Uint8Array | ArrayBuffer, usage: 'encrypt' | 'decrypt'): Promise<CryptoKey>;
102
+ /**
103
+ * Check whether a value is a WebCrypto CryptoKey without relying on the
104
+ * global `CryptoKey` constructor (absent in some runtimes).
105
+ */
106
+ export declare function isCryptoKey(value: unknown): value is CryptoKey;
107
+ /**
108
+ * Resolve a raw key (bytes or CryptoKey) to an AES-256-GCM CryptoKey.
109
+ * Returns an error message string when the key is unusable.
110
+ */
111
+ export declare function resolveRawKey(key: RawEncryptionKey, usage: 'encrypt' | 'decrypt'): Promise<CryptoKey | string>;
112
+ /** Whether a key value is a passphrase (selects kdf=pbkdf2). */
113
+ export declare function isPassphrase(key: unknown): key is string;
114
+ /** Whether a key value is raw key material (selects kdf=raw). */
115
+ export declare function isRawKey(key: unknown): key is RawEncryptionKey;
116
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/crypto/shared.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * @module errors
8
8
  */
9
- import type { ValidationError } from './types.js';
9
+ import type { DecryptionFailureReason, ValidationError } from './types.js';
10
10
  /**
11
11
  * WOLS error codes (stable, for programmatic handling).
12
12
  */
@@ -88,6 +88,36 @@ export declare class WolsValidationError extends WolsError {
88
88
  export declare class WolsEncryptionError extends WolsError {
89
89
  constructor(code: WolsErrorCode, message: string, details?: Record<string, unknown>);
90
90
  }
91
+ /**
92
+ * Decryption failure with a stable, typed {@link DecryptionFailureReason}.
93
+ *
94
+ * It extends `WolsEncryptionError` on purpose: 1.2.x returned a
95
+ * `WolsEncryptionError` for decryption failures, so existing
96
+ * `instanceof WolsEncryptionError` checks keep matching. Encryption misuse is
97
+ * thrown as a plain `WolsEncryptionError`; decryption failures are returned as
98
+ * this subclass, so check `instanceof WolsDecryptionError` to tell them apart.
99
+ * Malformed encrypted URLs are reported here too (`invalid-url`), not as
100
+ * `WolsParseError`, so every decryption input problem has one error type.
101
+ *
102
+ * Returned (never thrown) by `decryptSpecimen`, `decryptPlaintext`,
103
+ * `decryptField` and `decryptFields`. `reason` is also copied into
104
+ * `details.reason` for code that reads the generic error shape.
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * const r = await decryptSpecimen(url, { key });
109
+ * if (!r.success && r.error.reason === 'legacy-format') {
110
+ * // a pre-1.3.0 label: retry with { allowLegacy: true } during migration
111
+ * }
112
+ * ```
113
+ *
114
+ * @since 1.3.0
115
+ */
116
+ export declare class WolsDecryptionError extends WolsEncryptionError {
117
+ /** Stable failure category */
118
+ readonly reason: DecryptionFailureReason;
119
+ constructor(reason: DecryptionFailureReason, message: string, details?: Record<string, unknown>);
120
+ }
91
121
  /**
92
122
  * Create a required field error.
93
123
  */
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAMlD;;GAEG;AACH,oBAAY,aAAa;IACvB,0BAA0B;IAC1B,WAAW,qBAAqB;IAChC,0BAA0B;IAC1B,YAAY,sBAAsB;IAClC,8BAA8B;IAC9B,cAAc,wBAAwB;IACtC,wCAAwC;IACxC,eAAe,yBAAyB;IACxC,qCAAqC;IACrC,YAAY,sBAAsB;IAClC,gDAAgD;IAChD,iBAAiB,2BAA2B;IAC5C,+BAA+B;IAC/B,eAAe,yBAAyB;IACxC,oCAAoC;IACpC,qBAAqB,+BAA+B;IACpD,gCAAgC;IAChC,cAAc,wBAAwB;IACtC,kCAAkC;IAClC,mBAAmB,6BAA6B;IAChD,0CAA0C;IAC1C,kBAAkB,4BAA4B;IAC9C,iCAAiC;IACjC,WAAW,qBAAqB;IAChC,kCAAkC;IAClC,gBAAgB,0BAA0B;IAC1C,kCAAkC;IAClC,gBAAgB,0BAA0B;IAC1C,6BAA6B;IAC7B,WAAW,qBAAqB;IAChC,uCAAuC;IACvC,aAAa,uBAAuB;IACpC,mCAAmC;IACnC,aAAa,uBAAuB;CACrC;AAMD;;;GAGG;AACH,qBAAa,SAAU,SAAQ,KAAK;IAClC,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,+BAA+B;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;gBAE1C,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAYnF;;OAEG;IACH,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAQlC;AAED;;;GAGG;AACH,qBAAa,cAAe,SAAQ,SAAS;IAC3C,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;gBAGpC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE;CAU5D;AAED;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IAChD,iCAAiC;IACjC,QAAQ,CAAC,MAAM,EAAE,eAAe,EAAE,CAAC;gBAEvB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE;CASvD;AAED;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;gBACpC,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAQpF;AAMD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,MAAc,GAAG,mBAAmB,CAQ3F;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,MAAc,GACnB,mBAAmB,CAQrB;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,cAAc,CAoBvF"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAM3E;;GAEG;AACH,oBAAY,aAAa;IACvB,0BAA0B;IAC1B,WAAW,qBAAqB;IAChC,0BAA0B;IAC1B,YAAY,sBAAsB;IAClC,8BAA8B;IAC9B,cAAc,wBAAwB;IACtC,wCAAwC;IACxC,eAAe,yBAAyB;IACxC,qCAAqC;IACrC,YAAY,sBAAsB;IAClC,gDAAgD;IAChD,iBAAiB,2BAA2B;IAC5C,+BAA+B;IAC/B,eAAe,yBAAyB;IACxC,oCAAoC;IACpC,qBAAqB,+BAA+B;IACpD,gCAAgC;IAChC,cAAc,wBAAwB;IACtC,kCAAkC;IAClC,mBAAmB,6BAA6B;IAChD,0CAA0C;IAC1C,kBAAkB,4BAA4B;IAC9C,iCAAiC;IACjC,WAAW,qBAAqB;IAChC,kCAAkC;IAClC,gBAAgB,0BAA0B;IAC1C,kCAAkC;IAClC,gBAAgB,0BAA0B;IAC1C,6BAA6B;IAC7B,WAAW,qBAAqB;IAChC,uCAAuC;IACvC,aAAa,uBAAuB;IACpC,mCAAmC;IACnC,aAAa,uBAAuB;CACrC;AAMD;;;GAGG;AACH,qBAAa,SAAU,SAAQ,KAAK;IAClC,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,+BAA+B;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;gBAE1C,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAYnF;;OAEG;IACH,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAQlC;AAED;;;GAGG;AACH,qBAAa,cAAe,SAAQ,SAAS;IAC3C,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;gBAGpC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE;CAU5D;AAED;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IAChD,iCAAiC;IACjC,QAAQ,CAAC,MAAM,EAAE,eAAe,EAAE,CAAC;gBAEvB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE;CASvD;AAED;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;gBACpC,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAQpF;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,mBAAoB,SAAQ,mBAAmB;IAC1D,8BAA8B;IAC9B,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC;gBAE7B,MAAM,EAAE,uBAAuB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAahG;AAMD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,MAAc,GAAG,mBAAmB,CAQ3F;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,MAAc,GACnB,mBAAmB,CAQrB;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,cAAc,CAoBvF"}
@@ -1,26 +1,26 @@
1
1
  /**
2
2
  * @wemush/wols - Official WOLS TypeScript Library
3
3
  *
4
- * WeMush Open Labeling Standard (WOLS) v1.1.0 implementation
4
+ * WeMush Open Labeling Standard (WOLS) v1.3.0 implementation
5
5
  * for biological specimen labeling and traceability.
6
6
  *
7
7
  * @packageDocumentation
8
8
  * @module @wemush/wols
9
- * @version 1.1.0
9
+ * @version 1.3.0
10
10
  */
11
11
  export { WOLS_VERSION, WOLS_CONTEXT } from './types.js';
12
- export type { Brand, SpecimenId, SpecimenType, GrowthStage, Strain, Specimen, SpecimenInput, SpecimenMeta, ParseResult, ValidationResult, ValidationError, ValidationWarning, ValidationOptions, IdValidationMode, IdValidator, SpecimenRef, QRCodeOptions, EncryptionOptions, DecryptionOptions, EncryptedSpecimen, } from './types.js';
12
+ export type { Brand, SpecimenId, SpecimenType, GrowthStage, Strain, Specimen, SpecimenInput, SpecimenMeta, CreateSpecimenOptions, ParseResult, ValidationResult, ValidationError, ValidationWarning, ValidationOptions, IdValidationMode, IdValidator, SpecimenRef, QRCodeOptions, EncryptionOptions, DecryptionOptions, EncryptedSpecimen, EncryptedField, EncryptionKdf, RawEncryptionKey, DecryptionFailureReason, DecryptResult, FieldEncryptedSpecimen, FieldEncryptionOptions, } from './types.js';
13
13
  export { asSpecimenId, isSpecimenType, isGrowthStage, isStrain, SPECIMEN_TYPES, GROWTH_STAGES, } from './types.js';
14
- export { registerTypeAlias, resolveTypeAlias, getTypeAliases } from './types.js';
14
+ export { registerTypeAlias, resolveTypeAlias, getTypeAliases, resolveTypeAndStage, PRIMORDIA_TYPE_ALIAS, } from './types.js';
15
15
  export { WOLS_TO_PLATFORM_MAP, mapToWolsType, mapFromWolsType, registerPlatformType, } from './types.js';
16
16
  export { normalizeGeneration, isValidGeneration } from './types.js';
17
17
  export type { GenerationFormat } from './types.js';
18
- export { WolsErrorCode, WolsError, WolsParseError, WolsValidationError, WolsEncryptionError, } from './errors.js';
19
- export { createSpecimen, serializeSpecimen } from './specimen.js';
18
+ export { WolsErrorCode, WolsError, WolsParseError, WolsValidationError, WolsEncryptionError, WolsDecryptionError, } from './errors.js';
19
+ export { createSpecimen, serializeSpecimen, PRIMORDIA_AS_TYPE_WARNING } from './specimen.js';
20
20
  export { validateSpecimen } from './validator.js';
21
21
  export { parseSpecimen } from './parser.js';
22
22
  export { toCompactUrl, parseCompactUrl, parseCompactUrlOrThrow, parseCompactUrlOrNull, SPECIES_CODES, getSpeciesFromCode, getCodeFromSpecies, registerSpeciesCode, } from './compact-url.js';
23
- export { encryptSpecimen, isEncrypted, decryptSpecimen } from './crypto/index.js';
23
+ export { encryptSpecimen, encryptFields, isEncrypted, isEncryptedSpecimen, isEncryptedField, toEncryptedUrl, decryptSpecimen, decryptPlaintext, decryptField, decryptFields, parseEncryptedUrl, type FieldBinding, base64UrlEncode, base64UrlDecode, derivePassphraseKey, } from './crypto/index.js';
24
24
  export { toQRCode, toQRCodeDataURL, toQRCodeSVG, isQRAvailable, isQRAvailableSync, } from './qr/index.js';
25
25
  export { isServer, isBrowser, isWebWorker, isDeno, isBun, getRuntimeEnvironment, supportsWebCrypto, supportsNodeCrypto, isCryptoSupported, supportsTextEncoding, supportsURLAPIs, } from './utils/environment.js';
26
26
  export type { RuntimeEnvironment } from './utils/environment.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAKxD,YAAY,EAEV,KAAK,EACL,UAAU,EAGV,YAAY,EACZ,WAAW,EACX,MAAM,EACN,QAAQ,EACR,aAAa,EACb,YAAY,EAGZ,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EAGjB,gBAAgB,EAChB,WAAW,EAGX,WAAW,EAGX,aAAa,EAGb,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAKpB,OAAO,EACL,YAAY,EACZ,cAAc,EACd,aAAa,EACb,QAAQ,EACR,cAAc,EACd,aAAa,GACd,MAAM,YAAY,CAAC;AAKpB,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAKjF,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,eAAe,EACf,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAKpB,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAKnD,OAAO,EACL,aAAa,EACb,SAAS,EACT,cAAc,EACd,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAKrB,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAKlE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAKlD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAK5C,OAAO,EACL,YAAY,EACZ,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAK1B,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAKlF,OAAO,EACL,QAAQ,EACR,eAAe,EACf,WAAW,EACX,aAAa,EACb,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAKvB,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;AAChC,YAAY,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAKjE,OAAO,EACL,eAAe,EACf,UAAU,EACV,OAAO,EACP,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EACV,OAAO,EACP,aAAa,EACb,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAKxD,YAAY,EAEV,KAAK,EACL,UAAU,EAGV,YAAY,EACZ,WAAW,EACX,MAAM,EACN,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,qBAAqB,EAGrB,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EAGjB,gBAAgB,EAChB,WAAW,EAGX,WAAW,EAGX,aAAa,EAGb,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,uBAAuB,EACvB,aAAa,EACb,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAKpB,OAAO,EACL,YAAY,EACZ,cAAc,EACd,aAAa,EACb,QAAQ,EACR,cAAc,EACd,aAAa,GACd,MAAM,YAAY,CAAC;AAKpB,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAKpB,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,eAAe,EACf,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAKpB,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAKnD,OAAO,EACL,aAAa,EACb,SAAS,EACT,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAKrB,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAK7F,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAKlD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAK5C,OAAO,EACL,YAAY,EACZ,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAK1B,OAAO,EACL,eAAe,EACf,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,KAAK,YAAY,EACjB,eAAe,EACf,eAAe,EACf,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAK3B,OAAO,EACL,QAAQ,EACR,eAAe,EACf,WAAW,EACX,aAAa,EACb,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAKvB,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;AAChC,YAAY,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAKjE,OAAO,EACL,eAAe,EACf,UAAU,EACV,OAAO,EACP,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EACV,OAAO,EACP,aAAa,EACb,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -2,19 +2,31 @@
2
2
  * Specimen Creation and Serialization
3
3
  *
4
4
  * This module provides functions to create and serialize
5
- * WOLS v1.1.0 compliant specimen objects.
5
+ * WOLS v1.3.0 compliant specimen objects.
6
6
  *
7
7
  * @module specimen
8
8
  */
9
- import type { Specimen, SpecimenInput } from './types.js';
9
+ import type { CreateSpecimenOptions, Specimen, SpecimenInput } from './types.js';
10
+ /**
11
+ * Warning code emitted when `PRIMORDIA` is supplied as a specimen type.
12
+ *
13
+ * @since 1.3.0
14
+ */
15
+ export declare const PRIMORDIA_AS_TYPE_WARNING = "PRIMORDIA_AS_TYPE";
10
16
  /**
11
17
  * Create a new specimen with auto-generated ID and JSON-LD fields.
12
18
  *
13
- * This function creates a fully-formed WOLS v1.1.0 compliant Specimen
19
+ * This function creates a fully-formed WOLS v1.3.0 compliant Specimen
14
20
  * with automatic ID generation, version assignment, and JSON-LD context.
15
21
  *
22
+ * Type aliases resolve case-insensitively. `PRIMORDIA` supplied as a type
23
+ * resolves to `FRUITING`, sets `stage: 'PRIMORDIA'` when no stage was given
24
+ * (a supplied stage is never overridden), and emits a warning.
25
+ *
16
26
  * @param input - Specimen creation parameters
27
+ * @param options - `onWarning` receives non-fatal warnings (default: console.warn)
17
28
  * @returns Fully-formed Specimen with JSON-LD fields
29
+ * @throws Error if the type is unknown or `created` is not ISO 8601
18
30
  *
19
31
  * @example
20
32
  * ```typescript
@@ -44,9 +56,22 @@ import type { Specimen, SpecimenInput } from './types.js';
44
56
  * species: 'Ganoderma lucidum',
45
57
  * strain: 'Reishi Red', // Auto-expands to { name: 'Reishi Red' }
46
58
  * });
59
+ *
60
+ * // PRIMORDIA as a type (v1.3.0) -> type FRUITING, stage PRIMORDIA, plus a warning
61
+ * const pinning = createSpecimen(
62
+ * { type: 'PRIMORDIA', species: 'Pleurotus ostreatus' },
63
+ * { onWarning: w => console.info(w.message) }
64
+ * );
65
+ *
66
+ * // Explicit creation timestamp (v1.3.0)
67
+ * const dated = createSpecimen({
68
+ * type: 'CULTURE',
69
+ * species: 'Pleurotus ostreatus',
70
+ * created: '2025-12-16T10:30:00Z',
71
+ * });
47
72
  * ```
48
73
  */
49
- export declare function createSpecimen(input: SpecimenInput): Specimen;
74
+ export declare function createSpecimen(input: SpecimenInput, options?: CreateSpecimenOptions): Specimen;
50
75
  /**
51
76
  * Serialize a specimen to JSON-LD string.
52
77
  *
@@ -1 +1 @@
1
- {"version":3,"file":"specimen.d.ts","sourceRoot":"","sources":["../../src/specimen.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAU,MAAM,YAAY,CAAC;AAkClE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,QAAQ,CAsD7D;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAiD5D"}
1
+ {"version":3,"file":"specimen.d.ts","sourceRoot":"","sources":["../../src/specimen.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,qBAAqB,EACrB,QAAQ,EACR,aAAa,EAGd,MAAM,YAAY,CAAC;AAWpB;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,sBAAsB,CAAC;AAkC7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,aAAa,EACpB,OAAO,GAAE,qBAA0B,GAClC,QAAQ,CAmFV;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CA4D5D"}