@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.
Files changed (121) 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/compact-url.js +64 -0
  6. package/dist/cjs/compact-url.js.map +1 -1
  7. package/dist/cjs/crypto/decrypt.d.ts +110 -0
  8. package/dist/cjs/crypto/decrypt.d.ts.map +1 -0
  9. package/dist/cjs/crypto/decrypt.js +521 -90
  10. package/dist/cjs/crypto/decrypt.js.map +1 -1
  11. package/dist/cjs/crypto/encrypt.d.ts +132 -0
  12. package/dist/cjs/crypto/encrypt.d.ts.map +1 -0
  13. package/dist/cjs/crypto/encrypt.js +288 -105
  14. package/dist/cjs/crypto/encrypt.js.map +1 -1
  15. package/dist/cjs/crypto/index.d.ts +12 -0
  16. package/dist/cjs/crypto/index.d.ts.map +1 -0
  17. package/dist/cjs/crypto/index.js +15 -2
  18. package/dist/cjs/crypto/index.js.map +1 -1
  19. package/dist/cjs/crypto/shared.d.ts +116 -0
  20. package/dist/cjs/crypto/shared.d.ts.map +1 -0
  21. package/dist/cjs/crypto/shared.js +238 -0
  22. package/dist/cjs/crypto/shared.js.map +1 -0
  23. package/dist/cjs/errors.d.ts +133 -0
  24. package/dist/cjs/errors.d.ts.map +1 -0
  25. package/dist/cjs/errors.js +39 -1
  26. package/dist/cjs/errors.js.map +1 -1
  27. package/dist/cjs/index.d.ts +29 -0
  28. package/dist/cjs/index.d.ts.map +1 -0
  29. package/dist/cjs/index.js +70 -3
  30. package/dist/cjs/index.js.map +1 -1
  31. package/dist/cjs/package.json +3 -0
  32. package/dist/cjs/parser.d.ts +30 -0
  33. package/dist/cjs/parser.d.ts.map +1 -0
  34. package/dist/cjs/parser.js +3 -0
  35. package/dist/cjs/parser.js.map +1 -1
  36. package/dist/cjs/qr/generate.d.ts +62 -0
  37. package/dist/cjs/qr/generate.d.ts.map +1 -0
  38. package/dist/cjs/qr/index.d.ts +10 -0
  39. package/dist/cjs/qr/index.d.ts.map +1 -0
  40. package/dist/cjs/specimen.d.ts +96 -0
  41. package/dist/cjs/specimen.d.ts.map +1 -0
  42. package/dist/cjs/specimen.js +95 -9
  43. package/dist/cjs/specimen.js.map +1 -1
  44. package/dist/cjs/types.d.ts +735 -0
  45. package/dist/cjs/types.d.ts.map +1 -0
  46. package/dist/cjs/types.js +327 -3
  47. package/dist/cjs/types.js.map +1 -1
  48. package/dist/cjs/utils/cuid.d.ts +46 -0
  49. package/dist/cjs/utils/cuid.d.ts.map +1 -0
  50. package/dist/cjs/utils/cuid.js +6 -4
  51. package/dist/cjs/utils/cuid.js.map +1 -1
  52. package/dist/cjs/utils/environment.d.ts +137 -0
  53. package/dist/cjs/utils/environment.d.ts.map +1 -0
  54. package/dist/cjs/utils/environment.js +206 -0
  55. package/dist/cjs/utils/environment.js.map +1 -0
  56. package/dist/cjs/utils/iso8601.d.ts +79 -0
  57. package/dist/cjs/utils/iso8601.d.ts.map +1 -0
  58. package/dist/cjs/utils/migration.d.ts +162 -0
  59. package/dist/cjs/utils/migration.d.ts.map +1 -0
  60. package/dist/cjs/utils/migration.js +248 -0
  61. package/dist/cjs/utils/migration.js.map +1 -0
  62. package/dist/cjs/validator.d.ts +25 -0
  63. package/dist/cjs/validator.d.ts.map +1 -0
  64. package/dist/cjs/validator.js +85 -21
  65. package/dist/cjs/validator.js.map +1 -1
  66. package/dist/esm/compact-url.js +62 -0
  67. package/dist/esm/compact-url.js.map +1 -1
  68. package/dist/esm/crypto/decrypt.js +517 -90
  69. package/dist/esm/crypto/decrypt.js.map +1 -1
  70. package/dist/esm/crypto/encrypt.js +284 -105
  71. package/dist/esm/crypto/encrypt.js.map +1 -1
  72. package/dist/esm/crypto/index.js +5 -3
  73. package/dist/esm/crypto/index.js.map +1 -1
  74. package/dist/esm/crypto/shared.js +222 -0
  75. package/dist/esm/crypto/shared.js.map +1 -0
  76. package/dist/esm/errors.js +37 -0
  77. package/dist/esm/errors.js.map +1 -1
  78. package/dist/esm/index.js +26 -6
  79. package/dist/esm/index.js.map +1 -1
  80. package/dist/esm/parser.js +3 -0
  81. package/dist/esm/parser.js.map +1 -1
  82. package/dist/esm/specimen.js +95 -10
  83. package/dist/esm/specimen.js.map +1 -1
  84. package/dist/esm/types.js +317 -2
  85. package/dist/esm/types.js.map +1 -1
  86. package/dist/esm/utils/cuid.js +6 -4
  87. package/dist/esm/utils/cuid.js.map +1 -1
  88. package/dist/esm/utils/environment.js +193 -0
  89. package/dist/esm/utils/environment.js.map +1 -0
  90. package/dist/esm/utils/migration.js +237 -0
  91. package/dist/esm/utils/migration.js.map +1 -0
  92. package/dist/esm/validator.js +86 -22
  93. package/dist/esm/validator.js.map +1 -1
  94. package/dist/types/compact-url.d.ts +46 -0
  95. package/dist/types/compact-url.d.ts.map +1 -1
  96. package/dist/types/crypto/decrypt.d.ts +95 -18
  97. package/dist/types/crypto/decrypt.d.ts.map +1 -1
  98. package/dist/types/crypto/encrypt.d.ts +114 -14
  99. package/dist/types/crypto/encrypt.d.ts.map +1 -1
  100. package/dist/types/crypto/index.d.ts +5 -3
  101. package/dist/types/crypto/index.d.ts.map +1 -1
  102. package/dist/types/crypto/shared.d.ts +116 -0
  103. package/dist/types/crypto/shared.d.ts.map +1 -0
  104. package/dist/types/errors.d.ts +31 -1
  105. package/dist/types/errors.d.ts.map +1 -1
  106. package/dist/types/index.d.ts +15 -7
  107. package/dist/types/index.d.ts.map +1 -1
  108. package/dist/types/parser.d.ts.map +1 -1
  109. package/dist/types/specimen.d.ts +29 -4
  110. package/dist/types/specimen.d.ts.map +1 -1
  111. package/dist/types/types.d.ts +483 -17
  112. package/dist/types/types.d.ts.map +1 -1
  113. package/dist/types/utils/cuid.d.ts +5 -3
  114. package/dist/types/utils/cuid.d.ts.map +1 -1
  115. package/dist/types/utils/environment.d.ts +137 -0
  116. package/dist/types/utils/environment.d.ts.map +1 -0
  117. package/dist/types/utils/migration.d.ts +162 -0
  118. package/dist/types/utils/migration.d.ts.map +1 -0
  119. package/dist/types/validator.d.ts +1 -1
  120. package/dist/types/validator.d.ts.map +1 -1
  121. package/package.json +32 -14
@@ -1,33 +1,110 @@
1
1
  /**
2
- * WOLS Decryption Module
2
+ * WOLS Decryption Module (WOLS v1.3.0 encrypted format)
3
3
  *
4
- * Provides AES-256-GCM decryption for encrypted specimens using Web Crypto API.
5
- * Supports both password-based (PBKDF2) and raw CryptoKey decryption.
4
+ * Decrypts `wemush://v1/encrypted/...` URLs, the equivalent
5
+ * {@link EncryptedSpecimen} objects, and field-level {@link EncryptedField}
6
+ * values, using WebCrypto only.
7
+ *
8
+ * Failures never throw; they return `{ success: false, error }` where `error`
9
+ * is a {@link WolsDecryptionError} with a stable, typed `reason`.
10
+ *
11
+ * Every cheap check (parameter presence, encodings, IV length, iteration
12
+ * bounds, key type) runs before PBKDF2, so a malformed or hostile label costs
13
+ * no key derivation. The iteration count comes from the label and is bounded
14
+ * by `maxIterations` (default 10,000,000).
15
+ *
16
+ * Legacy payloads (no `kdf`, pre-1.3.0 fixed salt) are rejected with
17
+ * `legacy-format` unless `allowLegacy: true` is passed.
6
18
  *
7
19
  * @module crypto/decrypt
8
20
  */
9
- import type { Specimen, EncryptedSpecimen, DecryptionOptions, ParseResult } from '../types.js';
21
+ import type { DecryptResult, DecryptionOptions, EncryptedField, EncryptedSpecimen, FieldEncryptedSpecimen, Specimen } from '../types.js';
22
+ /**
23
+ * Parse an encrypted-format URL into an {@link EncryptedSpecimen} without decrypting.
24
+ *
25
+ * Only checks URL structure. Parameter rules (kdf, salt, iterations) are
26
+ * enforced on decryption. The path segment is percent-decoded to recover the ID.
27
+ *
28
+ * @since 1.3.0
29
+ */
30
+ export declare function parseEncryptedUrl(url: string): DecryptResult<EncryptedSpecimen>;
10
31
  /**
11
- * Decrypt an encrypted specimen using AES-256-GCM
32
+ * Decrypt an encrypted label to its exact plaintext (the embedded JSON-LD string).
12
33
  *
13
- * @param encrypted - The encrypted specimen wrapper
14
- * @param options - Decryption options (key required)
15
- * @returns Promise resolving to ParseResult with decrypted specimen or error
34
+ * Enforces every v1.3.0 wire rule, including that the decrypted specimen's
35
+ * `id` equals `wemush:{id}` from the URL path. For object input the check is
36
+ * made against the object's `id`; an object without `id` is not bound to any
37
+ * ID, so prefer the URL form for untrusted input.
38
+ *
39
+ * @param input - `wemush://v1/encrypted/...` URL or an {@link EncryptedSpecimen}
40
+ * @param options - `key`, and optional `allowLegacy` and `maxIterations`
41
+ * @since 1.3.0
42
+ */
43
+ export declare function decryptPlaintext(input: string | EncryptedSpecimen, options: DecryptionOptions): Promise<DecryptResult<string>>;
44
+ /**
45
+ * Decrypt an encrypted label and parse it into a validated Specimen.
46
+ *
47
+ * @param input - `wemush://v1/encrypted/...` URL or an {@link EncryptedSpecimen}
48
+ * @param options - `key`, and optional `allowLegacy` and `maxIterations`
49
+ * @returns DecryptResult; on failure `error.reason` gives the category
16
50
  *
17
51
  * @example
18
52
  * ```typescript
19
- * import { decryptSpecimen } from '@wemush/wols/crypto';
53
+ * const result = await decryptSpecimen(url, { key: 'correct horse battery staple' });
54
+ * if (result.success) console.log(result.data.species);
55
+ * else console.error(result.error.reason, result.error.message);
56
+ * ```
20
57
  *
21
- * const result = await decryptSpecimen(encrypted, {
22
- * key: 'my-secret-password',
23
- * });
58
+ * @since 1.3.0 Accepts encrypted URLs, raw key bytes, `allowLegacy` and `maxIterations`
59
+ */
60
+ export declare function decryptSpecimen(input: string | EncryptedSpecimen, options: DecryptionOptions): Promise<DecryptResult<Specimen>>;
61
+ /**
62
+ * Decrypt a single field-level {@link EncryptedField} to its original value.
24
63
  *
25
- * if (result.success) {
26
- * console.log(result.data);
27
- * } else {
28
- * console.error(result.error);
29
- * }
64
+ * `binding` names the label and field the value belongs to. It is the AES-GCM
65
+ * additional data (`"{id}\n{field}"`, WOLS 1.3.0), so a value moved to another
66
+ * field or copied from another label fails with `authentication-failed`.
67
+ * Legacy (pre-1.3.0) values, which have no `kdf`, carry no binding.
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * const r = await decryptField(partial.strain, { key }, { id: partial.id, field: 'strain' });
30
72
  * ```
73
+ *
74
+ * @since 1.3.0
75
+ */
76
+ export declare function decryptField(field: EncryptedField, options: DecryptionOptions, binding: FieldBinding): Promise<DecryptResult<unknown>>;
77
+ /** The label and top-level field an encrypted value belongs to. */
78
+ export interface FieldBinding {
79
+ /** The label's full id, e.g. `"wemush:clx1a2b3c4"` */
80
+ id: string;
81
+ /** The top-level field name, e.g. `"strain"` */
82
+ field: string;
83
+ }
84
+ /**
85
+ * Decrypt every field-level encrypted value in a specimen produced by
86
+ * `encryptFields()`, and return the restored, validated Specimen.
87
+ *
88
+ * Before any key is derived:
89
+ * - a label whose identifying fields (`@context`, `@type`, `id`, `version`,
90
+ * `type`, `species`) include an encrypted object is refused
91
+ * (`identifying-field`); decrypted values never replace them
92
+ * - every encrypted field is validated
93
+ * - the iterations of each distinct (salt, iterations) pair are summed, and
94
+ * the label is refused if the total exceeds `maxIterations` (10,000,000)
95
+ *
96
+ * Fields sharing a salt and iteration count share one derivation. Each value
97
+ * is authenticated against its AAD, `"{id}\n{field}"`, so swapped or copied
98
+ * values fail with `authentication-failed`.
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * const partial = await encryptFields(specimen, { key, fields: ['strain'] });
103
+ * const restored = await decryptFields(partial, { key });
104
+ * if (restored.success) restored.data.strain?.name;
105
+ * ```
106
+ *
107
+ * @since 1.3.0
31
108
  */
32
- export declare function decryptSpecimen(encrypted: EncryptedSpecimen, options: DecryptionOptions): Promise<ParseResult<Specimen>>;
109
+ export declare function decryptFields(value: FieldEncryptedSpecimen | Record<string, unknown>, options: DecryptionOptions): Promise<DecryptResult<Specimen>>;
33
110
  //# sourceMappingURL=decrypt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"decrypt.d.ts","sourceRoot":"","sources":["../../../src/crypto/decrypt.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAqE/F;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,iBAAiB,EAC5B,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAqDhC"}
1
+ {"version":3,"file":"decrypt.d.ts","sourceRoot":"","sources":["../../../src/crypto/decrypt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EAEjB,sBAAsB,EAEtB,QAAQ,EACT,MAAM,aAAa,CAAC;AAwDrB;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAAC,iBAAiB,CAAC,CAuE/E;AAwSD;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,MAAM,GAAG,iBAAiB,EACjC,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CA8BhC;AAeD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,GAAG,iBAAiB,EACjC,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAIlC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAiBjC;AAED,mEAAmE;AACnE,MAAM,WAAW,YAAY;IAC3B,sDAAsD;IACtD,EAAE,EAAE,MAAM,CAAC;IACX,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;CACf;AAsCD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,sBAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvD,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAyGlC"}
@@ -1,32 +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.
5
+ *
6
+ * ## Wire format
7
+ *
8
+ * ```
9
+ * wemush://v1/encrypted/{id}?e={ciphertext}&iv={iv}&kdf={pbkdf2|raw}[&salt={salt}&i={iterations}]
10
+ * ```
11
+ *
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
+ *
16
+ * ## Keys
17
+ *
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 |
23
+ *
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.
6
26
  *
7
27
  * @module crypto/encrypt
8
28
  */
9
- import type { Specimen, EncryptedSpecimen, EncryptionOptions } from '../types.js';
29
+ import type { EncryptedField, EncryptedSpecimen, EncryptionOptions, FieldEncryptedSpecimen, FieldEncryptedSpecimenOf, FieldEncryptionOptions, Specimen } from '../types.js';
10
30
  /**
11
- * Encrypt a specimen using AES-256-GCM
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).
51
+ *
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}.
12
55
  *
13
56
  * @param specimen - The specimen to encrypt
14
- * @param options - Encryption options (key required)
15
- * @returns Promise resolving to encrypted specimen wrapper
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
16
60
  *
17
61
  * @example
18
62
  * ```typescript
19
- * import { encryptSpecimen } from '@wemush/wols/crypto';
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'
20
66
  *
21
- * const encrypted = await encryptSpecimen(specimen, {
22
- * key: 'my-secret-password',
23
- * });
24
- * // Returns: { encrypted: true, payload: "...", iv: "...", algorithm: "AES-256-GCM" }
67
+ * // Raw 32-byte key: kdf=raw
68
+ * const raw = await encryptSpecimen(specimen, { key: crypto.getRandomValues(new Uint8Array(32)) });
25
69
  * ```
70
+ *
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()`.
26
73
  */
74
+ export declare function encryptSpecimen(specimen: Specimen, options: EncryptionOptions & {
75
+ fields: readonly string[];
76
+ }): Promise<FieldEncryptedSpecimen>;
27
77
  export declare function encryptSpecimen(specimen: Specimen, options: EncryptionOptions): Promise<EncryptedSpecimen>;
28
78
  /**
29
- * Type guard to check if an object is an encrypted specimen
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.
81
+ *
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()`.
86
+ *
87
+ * The identifying fields (`@context`, `@type`, `id`, `version`, `type`,
88
+ * `species`) can't be encrypted.
89
+ *
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
94
+ *
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
+ * ```
101
+ *
102
+ * @since 1.3.0
103
+ */
104
+ export declare function encryptFields<K extends keyof Specimen>(specimen: Specimen, options: FieldEncryptionOptions<K>): Promise<FieldEncryptedSpecimenOf<K>>;
105
+ /**
106
+ * Whether a value is a field-level {@link EncryptedField}: an encrypted value
107
+ * with a `kdf` and no specimen `id` or `url`.
108
+ *
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`).
114
+ *
115
+ * @since 1.3.0
116
+ */
117
+ export declare function isEncryptedField(value: unknown): value is EncryptedField;
118
+ /**
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`
30
130
  */
31
- export declare function isEncrypted(value: unknown): value is EncryptedSpecimen;
131
+ export declare function isEncrypted(value: unknown): value is EncryptedSpecimen | EncryptedField;
32
132
  //# sourceMappingURL=encrypt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"encrypt.d.ts","sourceRoot":"","sources":["../../../src/crypto/encrypt.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAwElF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,iBAAiB,CAAC,CAgC5B;AA+CD;;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":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,mBAAmB,EAAsC,MAAM,cAAc,CAAC;AACvF,OAAO,KAAK,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAM7E,2DAA2D;AAC3D,eAAO,MAAM,yBAAyB,SAAU,CAAC;AAEjD,uEAAuE;AACvE,eAAO,MAAM,wBAAwB,SAAU,CAAC;AAEhD,8EAA8E;AAC9E,eAAO,MAAM,uBAAuB,SAAU,CAAC;AAE/C,4EAA4E;AAC5E,eAAO,MAAM,qBAAqB,QAAS,CAAC;AAE5C,kFAAkF;AAClF,eAAO,MAAM,6BAA6B,WAAa,CAAC;AAExD;;;;GAIG;AACH,eAAO,MAAM,cAAc,WAAa,CAAC;AAEzC,4FAA4F;AAC5F,eAAO,MAAM,qBAAqB,aAAc,CAAC;AAEjD,uEAAuE;AACvE,eAAO,MAAM,WAAW,KAAK,CAAC;AAE9B,uFAAuF;AACvF,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC,kCAAkC;AAClC,eAAO,MAAM,SAAS,KAAK,CAAC;AAE5B,uCAAuC;AACvC,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC,4CAA4C;AAC5C,eAAO,MAAM,oBAAoB,2BAA2B,CAAC;AAE7D;;;GAGG;AACH,eAAO,MAAM,kBAAkB,yBAAqD,CAAC;AAMrF;;GAEG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,uBAAuB,EAC/B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACpC,mBAAmB,CAErB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAKtC;AAED,8CAA8C;AAC9C,eAAO,MAAM,oBAAoB,QAEkB,CAAC;AAEpD,gFAAgF;AAChF,wBAAgB,eAAe,IAAI,IAAI,CAItC;AAMD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,GAAG,MAAM,CAOvE;AAID;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,CAetE;AAMD,qDAAqD;AACrD,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,CAInE;AAcD;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,UAAU,GAAG,WAAW,EAC9B,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAoBlC;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,CAE3E;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAO/C,CAAC;AAEF,iDAAiD;AACjD,wBAAsB,YAAY,CAChC,KAAK,EAAE,UAAU,GAAG,WAAW,EAC/B,KAAK,EAAE,SAAS,GAAG,SAAS,GAC3B,OAAO,CAAC,SAAS,CAAC,CAEpB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAU9D;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,gBAAgB,EACrB,KAAK,EAAE,SAAS,GAAG,SAAS,GAC3B,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC,CAgB7B;AAED,gEAAgE;AAChE,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,MAAM,CAExD;AAED,iEAAiE;AACjE,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,gBAAgB,CAE9D"}
@@ -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,21 +1,29 @@
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, ParseResult, ValidationResult, ValidationError, ValidationWarning, ValidationOptions, 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 { WolsErrorCode, WolsError, WolsParseError, WolsValidationError, WolsEncryptionError, } from './errors.js';
15
- export { createSpecimen, serializeSpecimen } from './specimen.js';
14
+ export { registerTypeAlias, resolveTypeAlias, getTypeAliases, resolveTypeAndStage, PRIMORDIA_TYPE_ALIAS, } from './types.js';
15
+ export { WOLS_TO_PLATFORM_MAP, mapToWolsType, mapFromWolsType, registerPlatformType, } from './types.js';
16
+ export { normalizeGeneration, isValidGeneration } from './types.js';
17
+ export type { GenerationFormat } from './types.js';
18
+ export { WolsErrorCode, WolsError, WolsParseError, WolsValidationError, WolsEncryptionError, WolsDecryptionError, } from './errors.js';
19
+ export { createSpecimen, serializeSpecimen, PRIMORDIA_AS_TYPE_WARNING } from './specimen.js';
16
20
  export { validateSpecimen } from './validator.js';
17
21
  export { parseSpecimen } from './parser.js';
18
- export { toCompactUrl, parseCompactUrl, SPECIES_CODES, getSpeciesFromCode, getCodeFromSpecies, registerSpeciesCode, } from './compact-url.js';
19
- export { encryptSpecimen, isEncrypted, decryptSpecimen } from './crypto/index.js';
22
+ export { toCompactUrl, parseCompactUrl, parseCompactUrlOrThrow, parseCompactUrlOrNull, SPECIES_CODES, getSpeciesFromCode, getCodeFromSpecies, registerSpeciesCode, } from './compact-url.js';
23
+ export { encryptSpecimen, encryptFields, isEncrypted, isEncryptedSpecimen, isEncryptedField, toEncryptedUrl, decryptSpecimen, decryptPlaintext, decryptField, decryptFields, parseEncryptedUrl, type FieldBinding, base64UrlEncode, base64UrlDecode, derivePassphraseKey, } from './crypto/index.js';
20
24
  export { toQRCode, toQRCodeDataURL, toQRCodeSVG, isQRAvailable, isQRAvailableSync, } from './qr/index.js';
25
+ export { isServer, isBrowser, isWebWorker, isDeno, isBun, getRuntimeEnvironment, supportsWebCrypto, supportsNodeCrypto, isCryptoSupported, supportsTextEncoding, supportsURLAPIs, } from './utils/environment.js';
26
+ export type { RuntimeEnvironment } from './utils/environment.js';
27
+ export { compareVersions, isOutdated, isNewer, getCurrentVersion, registerMigration, canMigrate, migrate, getMigrations, clearMigrations, } from './utils/migration.js';
28
+ export type { VersionComparison, MigrationHandler } from './utils/migration.js';
21
29
  //# sourceMappingURL=index.d.ts.map
@@ -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,EAGb,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EAGjB,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,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,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"}
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"}
@@ -1 +1 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,QAAQ,EAId,MAAM,YAAY,CAAC;AAyCpB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,CAoGjE"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,QAAQ,EAKd,MAAM,YAAY,CAAC;AAyCpB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,CAwGjE"}
@@ -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;AA2BlE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,QAAQ,CAwC7D;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CA6C5D"}
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"}