@cardanowall/poe-standard 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/schema.d.ts CHANGED
@@ -1,93 +1,115 @@
1
1
  import { z } from 'zod';
2
2
 
3
- declare const ChunkedBytesArraySchema: z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
4
- type ChunkedBytesArray = z.infer<typeof ChunkedBytesArraySchema>;
5
- declare const UriChunkArraySchema: z.ZodArray<z.ZodString>;
6
- type UriChunkArray = z.infer<typeof UriChunkArraySchema>;
7
3
  declare const HashDigestSchema: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
8
4
  declare const HashesMapSchema: z.ZodRecord<z.ZodString, z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
9
5
  type HashesMap = z.infer<typeof HashesMapSchema>;
10
- declare const MerkleCommitSchema: z.ZodObject<{
6
+ declare const UriSchema: z.ZodString;
7
+ type Uri = z.infer<typeof UriSchema>;
8
+ declare const MerkleCommitSchema: z.ZodPipe<z.ZodCustom<unknown, unknown>, z.ZodObject<{
11
9
  alg: z.ZodString;
12
10
  root: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
13
- leaf_count: z.ZodNumber;
14
- uris: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString>>>;
15
- }, z.core.$strict>;
11
+ leaf_count: z.ZodUnion<readonly [z.ZodNumber, z.ZodBigInt]>;
12
+ uris: z.ZodOptional<z.ZodArray<z.ZodString>>;
13
+ }, z.core.$strict>>;
16
14
  type MerkleCommit = z.infer<typeof MerkleCommitSchema>;
17
- declare const SlotSchema: z.ZodObject<{
15
+ declare const SlotSchema: z.ZodPipe<z.ZodCustom<unknown, unknown>, z.ZodObject<{
18
16
  epk: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
19
- kem_ct: z.ZodOptional<z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>>;
17
+ kem_ct: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
20
18
  wrap: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
21
- }, z.core.$strip>;
19
+ }, z.core.$strip>>;
22
20
  type Slot = z.infer<typeof SlotSchema>;
23
- declare const Argon2idParamsSchema: z.ZodObject<{
24
- m: z.ZodNumber;
25
- t: z.ZodNumber;
26
- p: z.ZodNumber;
27
- }, z.core.$strict>;
21
+ declare const Argon2idParamsSchema: z.ZodPipe<z.ZodCustom<unknown, unknown>, z.ZodObject<{
22
+ m: z.ZodUnion<readonly [z.ZodNumber, z.ZodBigInt]>;
23
+ t: z.ZodUnion<readonly [z.ZodNumber, z.ZodBigInt]>;
24
+ p: z.ZodUnion<readonly [z.ZodNumber, z.ZodBigInt]>;
25
+ }, z.core.$strict>>;
28
26
  type Argon2idParams = z.infer<typeof Argon2idParamsSchema>;
29
- declare const PassphraseBlockSchema: z.ZodObject<{
27
+ declare const PassphraseBlockSchema: z.ZodPipe<z.ZodCustom<unknown, unknown>, z.ZodObject<{
30
28
  alg: z.ZodString;
31
29
  salt: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
32
30
  params: z.ZodRecord<z.ZodString, z.ZodUnknown>;
33
- }, z.core.$strict>;
31
+ }, z.core.$strict>>;
34
32
  type PassphraseBlock = z.infer<typeof PassphraseBlockSchema>;
35
- declare const EncryptionEnvelopeSchema: z.ZodObject<{
36
- scheme: z.ZodUnknown;
33
+ declare const EncScheme1Schema: z.ZodPipe<z.ZodCustom<unknown, unknown>, z.ZodObject<{
34
+ scheme: z.ZodLiteral<1>;
37
35
  aead: z.ZodString;
38
36
  kem: z.ZodOptional<z.ZodString>;
39
37
  nonce: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
40
- slots: z.ZodOptional<z.ZodArray<z.ZodObject<{
38
+ slots: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodCustom<unknown, unknown>, z.ZodObject<{
41
39
  epk: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
42
- kem_ct: z.ZodOptional<z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>>;
40
+ kem_ct: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
43
41
  wrap: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
44
- }, z.core.$strip>>>;
42
+ }, z.core.$strip>>>>;
45
43
  slots_mac: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
46
- passphrase: z.ZodOptional<z.ZodObject<{
44
+ passphrase: z.ZodOptional<z.ZodPipe<z.ZodCustom<unknown, unknown>, z.ZodObject<{
47
45
  alg: z.ZodString;
48
46
  salt: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
49
47
  params: z.ZodRecord<z.ZodString, z.ZodUnknown>;
50
- }, z.core.$strict>>;
51
- }, z.core.$strict>;
48
+ }, z.core.$strict>>>;
49
+ }, z.core.$strict>>;
50
+ type EncScheme1 = z.infer<typeof EncScheme1Schema>;
51
+ declare const EncOpaqueSchema: z.ZodPipe<z.ZodCustom<unknown, unknown>, z.ZodObject<{
52
+ scheme: z.ZodUnion<readonly [z.ZodNumber, z.ZodBigInt]>;
53
+ }, z.core.$loose>>;
54
+ type EncOpaque = z.infer<typeof EncOpaqueSchema>;
55
+ declare const EncryptionEnvelopeSchema: z.ZodUnion<readonly [z.ZodPipe<z.ZodCustom<unknown, unknown>, z.ZodObject<{
56
+ scheme: z.ZodLiteral<1>;
57
+ aead: z.ZodString;
58
+ kem: z.ZodOptional<z.ZodString>;
59
+ nonce: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
60
+ slots: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodCustom<unknown, unknown>, z.ZodObject<{
61
+ epk: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
62
+ kem_ct: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
63
+ wrap: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
64
+ }, z.core.$strip>>>>;
65
+ slots_mac: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
66
+ passphrase: z.ZodOptional<z.ZodPipe<z.ZodCustom<unknown, unknown>, z.ZodObject<{
67
+ alg: z.ZodString;
68
+ salt: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
69
+ params: z.ZodRecord<z.ZodString, z.ZodUnknown>;
70
+ }, z.core.$strict>>>;
71
+ }, z.core.$strict>>, z.ZodPipe<z.ZodCustom<unknown, unknown>, z.ZodObject<{
72
+ scheme: z.ZodUnion<readonly [z.ZodNumber, z.ZodBigInt]>;
73
+ }, z.core.$loose>>]>;
52
74
  type EncryptionEnvelope = z.infer<typeof EncryptionEnvelopeSchema>;
53
- declare const ItemEntrySchema: z.ZodObject<{
75
+ declare const ItemEntrySchema: z.ZodPipe<z.ZodCustom<unknown, unknown>, z.ZodObject<{
54
76
  hashes: z.ZodRecord<z.ZodString, z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
55
- uris: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString>>>;
77
+ uris: z.ZodOptional<z.ZodArray<z.ZodString>>;
56
78
  enc: z.ZodOptional<z.ZodUnknown>;
57
- }, z.core.$strict>;
79
+ }, z.core.$strict>>;
58
80
  type ItemEntry = z.infer<typeof ItemEntrySchema>;
59
- declare const SigEntrySchema: z.ZodObject<{
60
- cose_key: z.ZodOptional<z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>>;
61
- cose_sign1: z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
62
- }, z.core.$strict>;
81
+ declare const SigEntrySchema: z.ZodPipe<z.ZodCustom<unknown, unknown>, z.ZodObject<{
82
+ cose_key: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
83
+ cose_sign1: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
84
+ }, z.core.$strict>>;
63
85
  type SigEntry = z.infer<typeof SigEntrySchema>;
64
86
  declare const SupersedesSchema: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
65
87
  type Supersedes = z.infer<typeof SupersedesSchema>;
66
88
  declare const VersionLiteralSchema: z.ZodLiteral<1>;
67
- declare const PoeRecordSchema: z.ZodObject<{
89
+ declare const PoeRecordSchema: z.ZodPipe<z.ZodCustom<unknown, unknown>, z.ZodObject<{
68
90
  v: z.ZodLiteral<1>;
69
- items: z.ZodOptional<z.ZodArray<z.ZodObject<{
91
+ items: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodCustom<unknown, unknown>, z.ZodObject<{
70
92
  hashes: z.ZodRecord<z.ZodString, z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
71
- uris: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString>>>;
93
+ uris: z.ZodOptional<z.ZodArray<z.ZodString>>;
72
94
  enc: z.ZodOptional<z.ZodUnknown>;
73
- }, z.core.$strict>>>;
74
- merkle: z.ZodOptional<z.ZodArray<z.ZodObject<{
95
+ }, z.core.$strict>>>>;
96
+ merkle: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodCustom<unknown, unknown>, z.ZodObject<{
75
97
  alg: z.ZodString;
76
98
  root: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
77
- leaf_count: z.ZodNumber;
78
- uris: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString>>>;
79
- }, z.core.$strict>>>;
99
+ leaf_count: z.ZodUnion<readonly [z.ZodNumber, z.ZodBigInt]>;
100
+ uris: z.ZodOptional<z.ZodArray<z.ZodString>>;
101
+ }, z.core.$strict>>>>;
80
102
  supersedes: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
81
- sigs: z.ZodOptional<z.ZodArray<z.ZodObject<{
82
- cose_key: z.ZodOptional<z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>>;
83
- cose_sign1: z.ZodArray<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
84
- }, z.core.$strict>>>;
103
+ sigs: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodCustom<unknown, unknown>, z.ZodObject<{
104
+ cose_key: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
105
+ cose_sign1: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
106
+ }, z.core.$strict>>>>;
85
107
  crit: z.ZodOptional<z.ZodArray<z.ZodString>>;
86
- }, z.core.$loose>;
108
+ }, z.core.$loose>>;
87
109
  type PoeRecord = z.infer<typeof PoeRecordSchema>;
88
110
  declare const TOP_LEVEL_BASE_KEYS: ReadonlySet<string>;
89
111
  declare const EXTENSION_KEY_VENDOR_RE: RegExp;
90
112
  declare const EXTENSION_KEY_COMPANION_RE: RegExp;
91
113
  declare function isExtensionKey(k: string): boolean;
92
114
 
93
- export { type Argon2idParams, Argon2idParamsSchema, type ChunkedBytesArray, ChunkedBytesArraySchema, EXTENSION_KEY_COMPANION_RE, EXTENSION_KEY_VENDOR_RE, type EncryptionEnvelope, EncryptionEnvelopeSchema, HashDigestSchema, type HashesMap, HashesMapSchema, type ItemEntry, ItemEntrySchema, type MerkleCommit, MerkleCommitSchema, type PassphraseBlock, PassphraseBlockSchema, type PoeRecord, PoeRecordSchema, type SigEntry, SigEntrySchema, type Slot, SlotSchema, type Supersedes, SupersedesSchema, TOP_LEVEL_BASE_KEYS, type UriChunkArray, UriChunkArraySchema, VersionLiteralSchema, isExtensionKey };
115
+ export { type Argon2idParams, Argon2idParamsSchema, EXTENSION_KEY_COMPANION_RE, EXTENSION_KEY_VENDOR_RE, type EncOpaque, EncOpaqueSchema, type EncScheme1, EncScheme1Schema, type EncryptionEnvelope, EncryptionEnvelopeSchema, HashDigestSchema, type HashesMap, HashesMapSchema, type ItemEntry, ItemEntrySchema, type MerkleCommit, MerkleCommitSchema, type PassphraseBlock, PassphraseBlockSchema, type PoeRecord, PoeRecordSchema, type SigEntry, SigEntrySchema, type Slot, SlotSchema, type Supersedes, SupersedesSchema, TOP_LEVEL_BASE_KEYS, type Uri, UriSchema, VersionLiteralSchema, isExtensionKey };
package/dist/schema.js CHANGED
@@ -1,95 +1,108 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  // src/schema.ts
4
- var ChunkedBytesArraySchema = z.array(
5
- z.instanceof(Uint8Array).refine((b) => b.length >= 1 && b.length <= 64, {
6
- params: { code: "CHUNK_TOO_LARGE" }
7
- })
8
- ).min(1);
9
- var UTF8_ENCODER = new TextEncoder();
10
- var UriChunkArraySchema = z.array(
11
- z.string().refine(
12
- (s) => {
13
- const n = UTF8_ENCODER.encode(s).length;
14
- return n >= 1 && n <= 64;
15
- },
16
- { params: { code: "CHUNK_TOO_LARGE" } }
17
- )
18
- ).min(1);
4
+ function textKeyedMap(inner) {
5
+ return z.custom((value) => !(value instanceof Uint8Array), {
6
+ message: "CBOR byte string present where a text-keyed map is required"
7
+ }).pipe(inner);
8
+ }
19
9
  var HashDigestSchema = z.instanceof(Uint8Array);
20
10
  var HashesMapSchema = z.record(z.string(), HashDigestSchema);
21
- var MerkleCommitSchema = z.object({
22
- alg: z.string(),
23
- root: z.instanceof(Uint8Array),
24
- leaf_count: z.number().int().min(1),
25
- uris: z.array(UriChunkArraySchema).min(1).optional()
26
- }).strict();
27
- var SlotSchema = z.object({
28
- epk: z.instanceof(Uint8Array).optional(),
29
- kem_ct: ChunkedBytesArraySchema.optional(),
30
- wrap: z.instanceof(Uint8Array).optional()
31
- });
32
- var Argon2idParamsSchema = z.object({
33
- m: z.number().int(),
34
- t: z.number().int(),
35
- p: z.number().int()
36
- }).strict();
37
- var PassphraseBlockSchema = z.object({
38
- alg: z.string(),
39
- salt: z.instanceof(Uint8Array).superRefine((bytes, ctx) => {
40
- if (bytes.length < 16) {
41
- ctx.addIssue({
42
- code: "custom",
43
- path: [],
44
- message: `passphrase.salt length ${bytes.length} < 16`,
45
- params: { code: "ENC_PASSPHRASE_SALT_TOO_SHORT" }
46
- });
47
- } else if (bytes.length > 64) {
48
- ctx.addIssue({
49
- code: "custom",
50
- path: [],
51
- message: `passphrase.salt length ${bytes.length} > 64`,
52
- params: { code: "ENC_PASSPHRASE_SALT_TOO_LONG" }
53
- });
54
- }
55
- }),
56
- params: z.record(z.string(), z.unknown())
57
- }).strict();
58
- var EncryptionEnvelopeSchema = z.object({
59
- scheme: z.unknown(),
60
- aead: z.string(),
61
- kem: z.string().optional(),
62
- nonce: z.instanceof(Uint8Array),
63
- slots: z.array(SlotSchema).optional(),
64
- slots_mac: z.instanceof(Uint8Array).refine((b) => b.length === 32, {
65
- params: { code: "ENC_SLOTS_MAC_INVALID_LENGTH" }
66
- }).optional(),
67
- passphrase: PassphraseBlockSchema.optional()
68
- }).strict();
69
- var ItemEntrySchema = z.object({
70
- hashes: HashesMapSchema,
71
- uris: z.array(UriChunkArraySchema).min(1).optional(),
72
- // Captured as `unknown` so the validator can run the
73
- // `ENC_REQUIRES_CONTENT_HASH` pre-check ahead of any inner-shape errors
74
- // and surface the most informative code first.
75
- enc: z.unknown().optional()
76
- }).strict();
77
- var SigEntrySchema = z.object({
78
- cose_key: ChunkedBytesArraySchema.optional(),
79
- cose_sign1: ChunkedBytesArraySchema
80
- }).strict();
11
+ var UriSchema = z.string();
12
+ var MerkleCommitSchema = textKeyedMap(
13
+ z.object({
14
+ alg: z.string(),
15
+ root: z.instanceof(Uint8Array),
16
+ leaf_count: z.union([z.number().int(), z.bigint()]),
17
+ uris: z.array(UriSchema).optional()
18
+ }).strict()
19
+ );
20
+ var SlotSchema = textKeyedMap(
21
+ z.object({
22
+ epk: z.instanceof(Uint8Array).optional(),
23
+ kem_ct: z.instanceof(Uint8Array).optional(),
24
+ wrap: z.instanceof(Uint8Array).optional()
25
+ })
26
+ );
27
+ var Argon2idParamsSchema = textKeyedMap(
28
+ z.object({
29
+ m: z.union([z.number().int(), z.bigint()]),
30
+ t: z.union([z.number().int(), z.bigint()]),
31
+ p: z.union([z.number().int(), z.bigint()])
32
+ }).strict()
33
+ );
34
+ var PassphraseBlockSchema = textKeyedMap(
35
+ z.object({
36
+ alg: z.string(),
37
+ salt: z.instanceof(Uint8Array).superRefine((bytes, ctx) => {
38
+ if (bytes.length < 16) {
39
+ ctx.addIssue({
40
+ code: "custom",
41
+ path: [],
42
+ message: `passphrase.salt length ${bytes.length} < 16`,
43
+ params: { code: "ENC_PASSPHRASE_SALT_TOO_SHORT" }
44
+ });
45
+ } else if (bytes.length > 64) {
46
+ ctx.addIssue({
47
+ code: "custom",
48
+ path: [],
49
+ message: `passphrase.salt length ${bytes.length} > 64`,
50
+ params: { code: "ENC_PASSPHRASE_SALT_TOO_LONG" }
51
+ });
52
+ }
53
+ }),
54
+ params: z.record(z.string(), z.unknown())
55
+ }).strict()
56
+ );
57
+ var EncScheme1Schema = textKeyedMap(
58
+ z.object({
59
+ scheme: z.literal(1),
60
+ aead: z.string(),
61
+ kem: z.string().optional(),
62
+ nonce: z.instanceof(Uint8Array),
63
+ slots: z.array(SlotSchema).optional(),
64
+ slots_mac: z.instanceof(Uint8Array).refine((b) => b.length === 32, {
65
+ params: { code: "ENC_SLOTS_MAC_INVALID_LENGTH" }
66
+ }).optional(),
67
+ passphrase: PassphraseBlockSchema.optional()
68
+ }).strict()
69
+ );
70
+ var EncOpaqueSchema = textKeyedMap(
71
+ z.looseObject({
72
+ scheme: z.union([z.number().int().nonnegative(), z.bigint().nonnegative()])
73
+ })
74
+ );
75
+ var EncryptionEnvelopeSchema = z.union([EncScheme1Schema, EncOpaqueSchema]);
76
+ var ItemEntrySchema = textKeyedMap(
77
+ z.object({
78
+ hashes: HashesMapSchema,
79
+ uris: z.array(UriSchema).optional(),
80
+ // Captured as `unknown`: the envelope is a union whose disposition
81
+ // (typed scheme-1 vs opaque) depends on identifier support, so the
82
+ // validator's domain pass — not this schema — narrows it.
83
+ enc: z.unknown().optional()
84
+ }).strict()
85
+ );
86
+ var SigEntrySchema = textKeyedMap(
87
+ z.object({
88
+ cose_key: z.instanceof(Uint8Array).optional(),
89
+ cose_sign1: z.instanceof(Uint8Array)
90
+ }).strict()
91
+ );
81
92
  var SupersedesSchema = z.instanceof(Uint8Array).refine((b) => b.length === 32, {
82
93
  params: { code: "SUPERSEDES_TX_INVALID_LENGTH" }
83
94
  });
84
95
  var VersionLiteralSchema = z.literal(1);
85
- var PoeRecordSchema = z.looseObject({
86
- v: VersionLiteralSchema,
87
- items: z.array(ItemEntrySchema).optional(),
88
- merkle: z.array(MerkleCommitSchema).optional(),
89
- supersedes: SupersedesSchema.optional(),
90
- sigs: z.array(SigEntrySchema).optional(),
91
- crit: z.array(z.string()).optional()
92
- });
96
+ var PoeRecordSchema = textKeyedMap(
97
+ z.looseObject({
98
+ v: VersionLiteralSchema,
99
+ items: z.array(ItemEntrySchema).optional(),
100
+ merkle: z.array(MerkleCommitSchema).optional(),
101
+ supersedes: SupersedesSchema.optional(),
102
+ sigs: z.array(SigEntrySchema).optional(),
103
+ crit: z.array(z.string()).optional()
104
+ })
105
+ );
93
106
  var TOP_LEVEL_BASE_KEYS = /* @__PURE__ */ new Set([
94
107
  "v",
95
108
  "items",
@@ -98,12 +111,12 @@ var TOP_LEVEL_BASE_KEYS = /* @__PURE__ */ new Set([
98
111
  "sigs",
99
112
  "crit"
100
113
  ]);
101
- var EXTENSION_KEY_VENDOR_RE = /^x-.+\n?$/;
102
- var EXTENSION_KEY_COMPANION_RE = /^[a-z]+-.+\n?$/;
114
+ var EXTENSION_KEY_VENDOR_RE = /^x-[^\u0000-\u001f\u007f-\u009f]+$/;
115
+ var EXTENSION_KEY_COMPANION_RE = /^[a-z]+-[^\u0000-\u001f\u007f-\u009f]+$/;
103
116
  function isExtensionKey(k) {
104
117
  return EXTENSION_KEY_VENDOR_RE.test(k) || EXTENSION_KEY_COMPANION_RE.test(k);
105
118
  }
106
119
 
107
- export { Argon2idParamsSchema, ChunkedBytesArraySchema, EXTENSION_KEY_COMPANION_RE, EXTENSION_KEY_VENDOR_RE, EncryptionEnvelopeSchema, HashDigestSchema, HashesMapSchema, ItemEntrySchema, MerkleCommitSchema, PassphraseBlockSchema, PoeRecordSchema, SigEntrySchema, SlotSchema, SupersedesSchema, TOP_LEVEL_BASE_KEYS, UriChunkArraySchema, VersionLiteralSchema, isExtensionKey };
120
+ export { Argon2idParamsSchema, EXTENSION_KEY_COMPANION_RE, EXTENSION_KEY_VENDOR_RE, EncOpaqueSchema, EncScheme1Schema, EncryptionEnvelopeSchema, HashDigestSchema, HashesMapSchema, ItemEntrySchema, MerkleCommitSchema, PassphraseBlockSchema, PoeRecordSchema, SigEntrySchema, SlotSchema, SupersedesSchema, TOP_LEVEL_BASE_KEYS, UriSchema, VersionLiteralSchema, isExtensionKey };
108
121
  //# sourceMappingURL=schema.js.map
109
122
  //# sourceMappingURL=schema.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/schema.ts"],"names":[],"mappings":"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","file":"schema.js","sourcesContent":["// Label 309 v1 PoE record Zod schemas.\n//\n// Scope: structural shape gate. The schema enforces per-field types, length\n// bounds (chunk size, digest length, supersedes length, nonce length,\n// passphrase salt length), closed-map invariants (`sigs[i]`, `slot`,\n// `passphrase`, `merkle[i]`), and the `v == 1` literal. Cross-field rules\n// (item.hashes content-hash binding when `enc` present, slots/passphrase\n// exclusivity, `crit[]` shape, registry membership of algorithm\n// identifiers, COSE_Sign1 structural decode, URI per-scheme shape rules)\n// fire in `validator.ts` so the validator can emit the precise structural\n// codes (`UNSUPPORTED_*_ALG`, `ENC_*`, `SIG_*`, `INVALID_URI`,\n// `CRIT_SHAPE_INVALID`, …) rather than a generic schema-mismatch.\n//\n// Refinements that DO live in the schema (because the validator's domain\n// pass lifts these as `SCHEMA_*` / `*_LENGTH_MISMATCH` codes directly):\n// - chunk size `[1, 64]` → `CHUNK_TOO_LARGE`\n// - 32-byte digest / 32-byte root / 32-byte supersedes → `HASH_DIGEST_LENGTH_MISMATCH`\n// / `SUPERSEDES_TX_INVALID_LENGTH`\n// - 24-byte nonce / 32-byte slots_mac →\n// `NONCE_LENGTH_MISMATCH` / `ENC_SLOTS_MAC_INVALID_LENGTH`\n// - passphrase salt 16..64 bytes → `ENC_PASSPHRASE_SALT_TOO_SHORT` /\n// `ENC_PASSPHRASE_SALT_TOO_LONG`\n//\n// Per-slot recipient lengths (`epk`, `kem_ct`, `wrap`) are NOT enforced here:\n// the required slot shape depends on the envelope-level `kem`, which a slot\n// cannot see in isolation. The KEM-driven slot descriptor in `validator.ts`\n// emits the precise `KEM_EPK_LENGTH_MISMATCH` / `KEM_CT_LENGTH_MISMATCH` /\n// `WRAP_LENGTH_MISMATCH` / `ENC_SLOT_INVALID_SHAPE` codes instead.\n\nimport { z } from 'zod';\n\n// =============================================================================\n// Chunked-bytes / chunked-text arrays\n// =============================================================================\n\n// `[1* bstr .size (1..64)]`. A zero-length chunk (0 < 1) is rejected with the\n// SAME `CHUNK_TOO_LARGE` code as oversized chunks (any length outside\n// `[1, 64]`).\nexport const ChunkedBytesArraySchema = z\n .array(\n z.instanceof(Uint8Array).refine((b) => b.length >= 1 && b.length <= 64, {\n params: { code: 'CHUNK_TOO_LARGE' },\n }),\n )\n .min(1);\nexport type ChunkedBytesArray = z.infer<typeof ChunkedBytesArraySchema>;\n\n// `[1* tstr .size (1..64)]` — chunk byte length is the UTF-8-encoded length\n// (each `tstr` is wire-encoded as UTF-8). The `tstr .size (1..64)` pin is a\n// byte-count constraint, not a code-unit constraint.\nconst UTF8_ENCODER = new TextEncoder();\nexport const UriChunkArraySchema = z\n .array(\n z.string().refine(\n (s) => {\n const n = UTF8_ENCODER.encode(s).length;\n return n >= 1 && n <= 64;\n },\n { params: { code: 'CHUNK_TOO_LARGE' } },\n ),\n )\n .min(1);\nexport type UriChunkArray = z.infer<typeof UriChunkArraySchema>;\n\n// =============================================================================\n// Hashes map\n// =============================================================================\n//\n// `hashes` is a non-empty CBOR map keyed by content-hash algorithm identifier\n// (a CBOR text string from the content-hash registry) with the 32-byte digest\n// as value. cbor2 surfaces a text-keyed CBOR map as a plain JS object — z.record\n// admits any string key here. Both the registry-membership check\n// (`UNSUPPORTED_HASH_ALG`) and the per-algorithm digest-length check\n// (`HASH_DIGEST_LENGTH_MISMATCH`) live in the validator's domain pass so\n// each violation emits its precise code; the schema only enforces the\n// value is a CBOR byte string.\n\nexport const HashDigestSchema = z.instanceof(Uint8Array);\n\nexport const HashesMapSchema = z.record(z.string(), HashDigestSchema);\nexport type HashesMap = z.infer<typeof HashesMapSchema>;\n\n// =============================================================================\n// Top-level `merkle[]`\n// =============================================================================\n//\n// Each commit is a closed map `{alg, root, leaf_count, ? uris}`. `alg` is open\n// (registry membership is enforced in the validator's domain pass — unknown\n// identifiers emit `UNSUPPORTED_MERKLE_COMMIT_ALG`).\n\nexport const MerkleCommitSchema = z\n .object({\n alg: z.string(),\n root: z.instanceof(Uint8Array),\n leaf_count: z.number().int().min(1),\n uris: z.array(UriChunkArraySchema).min(1).optional(),\n })\n .strict();\nexport type MerkleCommit = z.infer<typeof MerkleCommitSchema>;\n\n// =============================================================================\n// Encryption envelope\n// =============================================================================\n\n// Per-slot recipient entry. The slot shape is KEM-driven:\n//\n// - x25519: `{ epk: bstr(32), wrap: bstr(48) }` — `epk` is the\n// ephemeral X25519 public key, `wrap` is the 32-byte CEK + 16-byte\n// ChaCha20-Poly1305 tag.\n// - mlkem768x25519: `{ kem_ct: [ bstr .size (1..64) ], wrap: bstr(48) }` —\n// `kem_ct` is the 1120-byte X-Wing `enc` carried as a chunked byte-string\n// array (the same `bytes-chunk-array` shape `sigs[i].cose_sign1` uses);\n// there is NO per-slot `epk` on the hybrid path.\n//\n// The `kem` identifier is hoisted to envelope scope (a per-slot `kem` would\n// be wire-bloat). The schema is deliberately PERMISSIVE:\n// `epk`, `kem_ct`, and `wrap` are all optional and `.strict()` is NOT applied.\n// Both the per-field length checks (`KEM_EPK_LENGTH_MISMATCH`,\n// `KEM_CT_LENGTH_MISMATCH`, `WRAP_LENGTH_MISMATCH`) and the KEM-driven\n// shape gate (which field MUST/MUST NOT be present for the declared `kem`,\n// emitting `ENC_SLOT_INVALID_SHAPE`) live in the validator's domain pass —\n// the structural schema cannot know the envelope `kem` from a slot in\n// isolation, and we want the precise KEM-aware code rather than a generic\n// schema mismatch. Because `.strict()` is dropped, the domain pass MUST\n// explicitly reject cross-KEM contamination (an x25519 slot carrying\n// `kem_ct`, or a hybrid slot carrying `epk`).\nexport const SlotSchema = z.object({\n epk: z.instanceof(Uint8Array).optional(),\n kem_ct: ChunkedBytesArraySchema.optional(),\n wrap: z.instanceof(Uint8Array).optional(),\n});\nexport type Slot = z.infer<typeof SlotSchema>;\n\n// Argon2id params `{m, t, p}` are a closed map. Each value MUST be a CBOR\n// unsigned integer; the FLOOR check (`m ≥ 65536`,\n// `t ≥ 3`, `p ≥ 1`) emits `ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW` in the\n// validator's domain pass — keeping it out of the schema lets us emit the\n// distinct salt-length code when salt itself is malformed too.\nexport const Argon2idParamsSchema = z\n .object({\n m: z.number().int(),\n t: z.number().int(),\n p: z.number().int(),\n })\n .strict();\nexport type Argon2idParams = z.infer<typeof Argon2idParamsSchema>;\n\n// Passphrase block. `alg` is open (registry membership checked in the\n// validator's domain pass → `ENC_PASSPHRASE_ALG_UNSUPPORTED`);\n// `params` is open here (validator narrows on the registered `alg` value and\n// emits `SCHEMA_UNKNOWN_FIELD` for extra keys, `ENC_PASSPHRASE_ARGON2_PARAMS_TOO_LOW`\n// for sub-floor values). `salt` length floor/ceiling are schema-layer\n// refinements with the dedicated `ENC_PASSPHRASE_SALT_TOO_SHORT/TOO_LONG`\n// codes — they belong at the schema layer because a slot cannot otherwise\n// see the salt length.\nexport const PassphraseBlockSchema = z\n .object({\n alg: z.string(),\n salt: z.instanceof(Uint8Array).superRefine((bytes, ctx) => {\n if (bytes.length < 16) {\n ctx.addIssue({\n code: 'custom',\n path: [],\n message: `passphrase.salt length ${bytes.length} < 16`,\n params: { code: 'ENC_PASSPHRASE_SALT_TOO_SHORT' },\n });\n } else if (bytes.length > 64) {\n ctx.addIssue({\n code: 'custom',\n path: [],\n message: `passphrase.salt length ${bytes.length} > 64`,\n params: { code: 'ENC_PASSPHRASE_SALT_TOO_LONG' },\n });\n }\n }),\n params: z.record(z.string(), z.unknown()),\n })\n .strict();\nexport type PassphraseBlock = z.infer<typeof PassphraseBlockSchema>;\n\n// Sealed-PoE envelope. The wire format admits any combination of\n// `kem` / `slots` / `slots_mac` / `passphrase` keys (permissive superset);\n// cross-field invariants (slots ⊕ passphrase, slots ↔ slots_mac, slots\n// requires kem, content-hash binding, slots non-empty) are enforced in the\n// validator's domain pass so each violation emits its typed code rather\n// than a generic shape mismatch.\nexport const EncryptionEnvelopeSchema = z\n .object({\n scheme: z.unknown(),\n aead: z.string(),\n kem: z.string().optional(),\n nonce: z.instanceof(Uint8Array),\n slots: z.array(SlotSchema).optional(),\n slots_mac: z\n .instanceof(Uint8Array)\n .refine((b) => b.length === 32, {\n params: { code: 'ENC_SLOTS_MAC_INVALID_LENGTH' },\n })\n .optional(),\n passphrase: PassphraseBlockSchema.optional(),\n })\n .strict();\nexport type EncryptionEnvelope = z.infer<typeof EncryptionEnvelopeSchema>;\n\n// =============================================================================\n// Item entry\n// =============================================================================\n\nexport const ItemEntrySchema = z\n .object({\n hashes: HashesMapSchema,\n uris: z.array(UriChunkArraySchema).min(1).optional(),\n // Captured as `unknown` so the validator can run the\n // `ENC_REQUIRES_CONTENT_HASH` pre-check ahead of any inner-shape errors\n // and surface the most informative code first.\n enc: z.unknown().optional(),\n })\n .strict();\nexport type ItemEntry = z.infer<typeof ItemEntrySchema>;\n\n// =============================================================================\n// Sig entry\n// =============================================================================\n//\n// Closed CBOR map `{cose_sign1, ? cose_key}`. Canonical CBOR map-key sort\n// (RFC 8949 §4.2.1, bytewise lex on encoded keys) places `cose_key`\n// (length-8 tstr, `0x68`) BEFORE `cose_sign1` (length-10 tstr, `0x6a`); the\n// schema property-order is irrelevant — the canonical encoder handles it.\nexport const SigEntrySchema = z\n .object({\n cose_key: ChunkedBytesArraySchema.optional(),\n cose_sign1: ChunkedBytesArraySchema,\n })\n .strict();\nexport type SigEntry = z.infer<typeof SigEntrySchema>;\n\n// =============================================================================\n// Supersedence\n// =============================================================================\n\nexport const SupersedesSchema = z.instanceof(Uint8Array).refine((b) => b.length === 32, {\n params: { code: 'SUPERSEDES_TX_INVALID_LENGTH' },\n});\nexport type Supersedes = z.infer<typeof SupersedesSchema>;\n\n// =============================================================================\n// Top-level record\n// =============================================================================\n//\n// `v == 1` is a literal — a future major (`v: 2`) MUST be rejected with\n// `SCHEMA_INVALID_LITERAL`. `z.literal(1)` preserves the narrow `1` type for\n// the inferred `PoeRecord[\"v\"]` (so consumers can dispatch on it) and emits\n// Zod's `invalid_value` code which the validator's mapper lifts to\n// `SCHEMA_INVALID_LITERAL`.\n//\n// `looseObject` admits extension keys (matching `^x-.+` or `^[a-z]+-.+`); the\n// validator's domain pass rejects unknown keys that match neither pattern with\n// `SCHEMA_UNKNOWN_FIELD`.\nexport const VersionLiteralSchema = z.literal(1);\n\nexport const PoeRecordSchema = z.looseObject({\n v: VersionLiteralSchema,\n items: z.array(ItemEntrySchema).optional(),\n merkle: z.array(MerkleCommitSchema).optional(),\n supersedes: SupersedesSchema.optional(),\n sigs: z.array(SigEntrySchema).optional(),\n crit: z.array(z.string()).optional(),\n});\nexport type PoeRecord = z.infer<typeof PoeRecordSchema>;\n\n// =============================================================================\n// Closed top-level base-key registry\n// =============================================================================\n//\n// Used by the validator's domain pass to distinguish unknown-typo keys from\n// well-formed extension keys (`^x-.+` / `^[a-z]+-.+`).\nexport const TOP_LEVEL_BASE_KEYS: ReadonlySet<string> = new Set([\n 'v',\n 'items',\n 'merkle',\n 'supersedes',\n 'sigs',\n 'crit',\n]);\n\n// Extension-key namespaces. Anchored at both ends so an\n// embedded newline cannot smuggle a multi-segment key past the check: `.`\n// excludes `\\n` in JS, and the `\\n?$` tail tolerates exactly ONE trailing\n// newline (matching the Python validator's `re.fullmatch(r'^(x-.+|[a-z]+-.+)$')`\n// semantics, where `$` likewise admits a single trailing `\\n`). So `x-note\\n`\n// is an extension key, but `x-a\\nb`, `x-note\\n\\n`, and `x-\\n` are not.\nexport const EXTENSION_KEY_VENDOR_RE = /^x-.+\\n?$/;\nexport const EXTENSION_KEY_COMPANION_RE = /^[a-z]+-.+\\n?$/;\n\nexport function isExtensionKey(k: string): boolean {\n return EXTENSION_KEY_VENDOR_RE.test(k) || EXTENSION_KEY_COMPANION_RE.test(k);\n}\n"]}
1
+ {"version":3,"sources":["../src/schema.ts"],"names":[],"mappings":";;;AAsCA,SAAS,aAAkC,KAAA,EAAU;AACnD,EAAA,OAAO,EACJ,MAAA,CAAgB,CAAC,KAAA,KAAU,EAAE,iBAAiB,UAAA,CAAA,EAAa;AAAA,IAC1D,OAAA,EAAS;AAAA,GACV,CAAA,CACA,IAAA,CAAK,KAAK,CAAA;AACf;AAcO,IAAM,gBAAA,GAAmB,CAAA,CAAE,UAAA,CAAW,UAAU;AAEhD,IAAM,kBAAkB,CAAA,CAAE,MAAA,CAAO,CAAA,CAAE,MAAA,IAAU,gBAAgB;AAU7D,IAAM,SAAA,GAAY,EAAE,MAAA;AAepB,IAAM,kBAAA,GAAqB,YAAA;AAAA,EAChC,EACG,MAAA,CAAO;AAAA,IACN,GAAA,EAAK,EAAE,MAAA,EAAO;AAAA,IACd,IAAA,EAAM,CAAA,CAAE,UAAA,CAAW,UAAU,CAAA;AAAA,IAC7B,UAAA,EAAY,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,EAAI,EAAG,CAAA,CAAE,MAAA,EAAQ,CAAC,CAAA;AAAA,IAClD,IAAA,EAAM,CAAA,CAAE,KAAA,CAAM,SAAS,EAAE,QAAA;AAAS,GACnC,EACA,MAAA;AACL;AAwCO,IAAM,UAAA,GAAa,YAAA;AAAA,EACxB,EAAE,MAAA,CAAO;AAAA,IACP,GAAA,EAAK,CAAA,CAAE,UAAA,CAAW,UAAU,EAAE,QAAA,EAAS;AAAA,IACvC,MAAA,EAAQ,CAAA,CAAE,UAAA,CAAW,UAAU,EAAE,QAAA,EAAS;AAAA,IAC1C,IAAA,EAAM,CAAA,CAAE,UAAA,CAAW,UAAU,EAAE,QAAA;AAAS,GACzC;AACH;AAOO,IAAM,oBAAA,GAAuB,YAAA;AAAA,EAClC,EACG,MAAA,CAAO;AAAA,IACN,CAAA,EAAG,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,EAAI,EAAG,CAAA,CAAE,MAAA,EAAQ,CAAC,CAAA;AAAA,IACzC,CAAA,EAAG,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,EAAI,EAAG,CAAA,CAAE,MAAA,EAAQ,CAAC,CAAA;AAAA,IACzC,CAAA,EAAG,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,EAAI,EAAG,CAAA,CAAE,MAAA,EAAQ,CAAC;AAAA,GAC1C,EACA,MAAA;AACL;AASO,IAAM,qBAAA,GAAwB,YAAA;AAAA,EACnC,EACG,MAAA,CAAO;AAAA,IACN,GAAA,EAAK,EAAE,MAAA,EAAO;AAAA,IACd,IAAA,EAAM,EAAE,UAAA,CAAW,UAAU,EAAE,WAAA,CAAY,CAAC,OAAO,GAAA,KAAQ;AACzD,MAAA,IAAI,KAAA,CAAM,SAAS,EAAA,EAAI;AACrB,QAAA,GAAA,CAAI,QAAA,CAAS;AAAA,UACX,IAAA,EAAM,QAAA;AAAA,UACN,MAAM,EAAC;AAAA,UACP,OAAA,EAAS,CAAA,uBAAA,EAA0B,KAAA,CAAM,MAAM,CAAA,KAAA,CAAA;AAAA,UAC/C,MAAA,EAAQ,EAAE,IAAA,EAAM,+BAAA;AAAgC,SACjD,CAAA;AAAA,MACH,CAAA,MAAA,IAAW,KAAA,CAAM,MAAA,GAAS,EAAA,EAAI;AAC5B,QAAA,GAAA,CAAI,QAAA,CAAS;AAAA,UACX,IAAA,EAAM,QAAA;AAAA,UACN,MAAM,EAAC;AAAA,UACP,OAAA,EAAS,CAAA,uBAAA,EAA0B,KAAA,CAAM,MAAM,CAAA,KAAA,CAAA;AAAA,UAC/C,MAAA,EAAQ,EAAE,IAAA,EAAM,8BAAA;AAA+B,SAChD,CAAA;AAAA,MACH;AAAA,IACF,CAAC,CAAA;AAAA,IACD,MAAA,EAAQ,EAAE,MAAA,CAAO,CAAA,CAAE,QAAO,EAAG,CAAA,CAAE,SAAS;AAAA,GACzC,EACA,MAAA;AACL;AAUO,IAAM,gBAAA,GAAmB,YAAA;AAAA,EAC9B,EACG,MAAA,CAAO;AAAA,IACN,MAAA,EAAQ,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAA;AAAA,IACnB,IAAA,EAAM,EAAE,MAAA,EAAO;AAAA,IACf,GAAA,EAAK,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,IACzB,KAAA,EAAO,CAAA,CAAE,UAAA,CAAW,UAAU,CAAA;AAAA,IAC9B,KAAA,EAAO,CAAA,CAAE,KAAA,CAAM,UAAU,EAAE,QAAA,EAAS;AAAA,IACpC,SAAA,EAAW,CAAA,CACR,UAAA,CAAW,UAAU,CAAA,CACrB,OAAO,CAAC,CAAA,KAAM,CAAA,CAAE,MAAA,KAAW,EAAA,EAAI;AAAA,MAC9B,MAAA,EAAQ,EAAE,IAAA,EAAM,8BAAA;AAA+B,KAChD,EACA,QAAA,EAAS;AAAA,IACZ,UAAA,EAAY,sBAAsB,QAAA;AAAS,GAC5C,EACA,MAAA;AACL;AAOO,IAAM,eAAA,GAAkB,YAAA;AAAA,EAC7B,EAAE,WAAA,CAAY;AAAA,IACZ,QAAQ,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,CAAE,QAAO,CAAE,GAAA,EAAI,CAAE,WAAA,IAAe,CAAA,CAAE,MAAA,EAAO,CAAE,WAAA,EAAa,CAAC;AAAA,GAC3E;AACH;AAGO,IAAM,2BAA2B,CAAA,CAAE,KAAA,CAAM,CAAC,gBAAA,EAAkB,eAAe,CAAC;AAO5E,IAAM,eAAA,GAAkB,YAAA;AAAA,EAC7B,EACG,MAAA,CAAO;AAAA,IACN,MAAA,EAAQ,eAAA;AAAA,IACR,IAAA,EAAM,CAAA,CAAE,KAAA,CAAM,SAAS,EAAE,QAAA,EAAS;AAAA;AAAA;AAAA;AAAA,IAIlC,GAAA,EAAK,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAA;AAAS,GAC3B,EACA,MAAA;AACL;AAWO,IAAM,cAAA,GAAiB,YAAA;AAAA,EAC5B,EACG,MAAA,CAAO;AAAA,IACN,QAAA,EAAU,CAAA,CAAE,UAAA,CAAW,UAAU,EAAE,QAAA,EAAS;AAAA,IAC5C,UAAA,EAAY,CAAA,CAAE,UAAA,CAAW,UAAU;AAAA,GACpC,EACA,MAAA;AACL;AAOO,IAAM,gBAAA,GAAmB,CAAA,CAAE,UAAA,CAAW,UAAU,CAAA,CAAE,OAAO,CAAC,CAAA,KAAM,CAAA,CAAE,MAAA,KAAW,EAAA,EAAI;AAAA,EACtF,MAAA,EAAQ,EAAE,IAAA,EAAM,8BAAA;AAClB,CAAC;AAeM,IAAM,oBAAA,GAAuB,CAAA,CAAE,OAAA,CAAQ,CAAC;AAExC,IAAM,eAAA,GAAkB,YAAA;AAAA,EAC7B,EAAE,WAAA,CAAY;AAAA,IACZ,CAAA,EAAG,oBAAA;AAAA,IACH,KAAA,EAAO,CAAA,CAAE,KAAA,CAAM,eAAe,EAAE,QAAA,EAAS;AAAA,IACzC,MAAA,EAAQ,CAAA,CAAE,KAAA,CAAM,kBAAkB,EAAE,QAAA,EAAS;AAAA,IAC7C,UAAA,EAAY,iBAAiB,QAAA,EAAS;AAAA,IACtC,IAAA,EAAM,CAAA,CAAE,KAAA,CAAM,cAAc,EAAE,QAAA,EAAS;AAAA,IACvC,MAAM,CAAA,CAAE,KAAA,CAAM,EAAE,MAAA,EAAQ,EAAE,QAAA;AAAS,GACpC;AACH;AASO,IAAM,mBAAA,uBAA+C,GAAA,CAAI;AAAA,EAC9D,GAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,YAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAAC;AAWM,IAAM,uBAAA,GAA0B;AAEhC,IAAM,0BAAA,GAA6B;AAEnC,SAAS,eAAe,CAAA,EAAoB;AACjD,EAAA,OAAO,wBAAwB,IAAA,CAAK,CAAC,CAAA,IAAK,0BAAA,CAA2B,KAAK,CAAC,CAAA;AAC7E","file":"schema.js","sourcesContent":["// Label 309 v1 PoE record Zod schemas.\n//\n// Scope: structural shape gate over the DECODED record body. The schema\n// enforces per-field CBOR types (every map position rejects a CBOR byte\n// string — see the text-keyed-map gate below), the fixed byte lengths a field\n// can assert in isolation (32-byte `supersedes`, 32-byte `slots_mac`, the\n// 16..64-byte passphrase salt), closed-map invariants (`items[i]`,\n// `merkle[i]`, `sigs[i]`, `passphrase`), and the `v == 1` literal.\n// Cross-field rules (content-hash binding under `enc`, slots/passphrase\n// exclusivity, `crit[]` shape, registry membership of algorithm identifiers,\n// COSE_Sign1 structural decode, URI shape, non-empty-array rules, integer\n// ranges) fire in `validator.ts` so each violation emits its precise\n// canonical code rather than a generic schema mismatch.\n//\n// Every logical byte string is a SINGLE CBOR byte string and every URI is a\n// SINGLE text string: record-body fields carry no 64-byte cap and no chunk\n// wrappers. The ledger's 64-byte metadata-string cap is satisfied by the\n// whole-body transport chunk array alone (see `carriage.ts`), which is\n// reassembled before the validator ever sees the body.\n\nimport { z } from 'zod';\n\n// =============================================================================\n// Text-keyed-map gate\n// =============================================================================\n//\n// Every map position in the record grammar is a CBOR text-keyed map. The\n// canonical decoder surfaces such a map as a plain object — but a CBOR byte\n// string surfaces as a Uint8Array, which is ALSO an object to Zod, so an\n// ungated object schema would walk its byte indices as if they were map keys\n// and mis-report one unknown-key issue per byte plus a missing-required issue\n// per absent field. The gate runs ahead of the map schema and fails a byte\n// string with a SINGLE issue at the position itself — the same one-issue\n// rejection every other non-map CBOR shape already receives from the object\n// parser; the validator's issue mapper then lifts it to the position's\n// canonical code by path. (`z.record` positions need no gate: Zod's record\n// parser rejects a Uint8Array outright. A map carrying non-text keys decodes\n// to a `Map` and is handled by the validator's pre-guard, not here.)\nfunction textKeyedMap<S extends z.ZodType>(inner: S) {\n return z\n .custom<unknown>((value) => !(value instanceof Uint8Array), {\n message: 'CBOR byte string present where a text-keyed map is required',\n })\n .pipe(inner);\n}\n\n// =============================================================================\n// Hashes map\n// =============================================================================\n//\n// `hashes` is a non-empty CBOR map keyed by content-hash algorithm identifier\n// (a text string from the content-hash registry) with the 32-byte digest as\n// value. The canonical decoder surfaces a text-keyed CBOR map as a plain JS\n// object — `z.record` admits any string key here. Registry membership\n// (`UNSUPPORTED_HASH_ALG`), per-algorithm digest length\n// (`HASH_DIGEST_LENGTH_MISMATCH`), and the non-empty rule live in the\n// validator's domain pass.\n\nexport const HashDigestSchema = z.instanceof(Uint8Array);\n\nexport const HashesMapSchema = z.record(z.string(), HashDigestSchema);\nexport type HashesMap = z.infer<typeof HashesMapSchema>;\n\n// =============================================================================\n// URIs\n// =============================================================================\n//\n// One absolute URI in a single text string. Absolute-URI / fragment / scheme\n// / per-scheme body rules (`INVALID_URI`) are domain checks.\n\nexport const UriSchema = z.string();\nexport type Uri = z.infer<typeof UriSchema>;\n\n// =============================================================================\n// Top-level `merkle[]`\n// =============================================================================\n//\n// Each commit is a closed map `{alg, root, leaf_count, ? uris}`. `alg` is open\n// (registry membership → `UNSUPPORTED_MERKLE_COMMIT_ALG` in the domain pass).\n// `leaf_count` is a CBOR unsigned integer pinned to `1 .. 2^32 − 1`, handled\n// as an EXACT integer: the canonical decoder surfaces values above 2^53 − 1 as\n// `bigint`, so the schema admits both representations and the domain pass\n// enforces the range (`SCHEMA_MERKLE_LEAF_COUNT_INVALID`) without ever\n// rounding through a float.\n\nexport const MerkleCommitSchema = textKeyedMap(\n z\n .object({\n alg: z.string(),\n root: z.instanceof(Uint8Array),\n leaf_count: z.union([z.number().int(), z.bigint()]),\n uris: z.array(UriSchema).optional(),\n })\n .strict(),\n);\nexport type MerkleCommit = z.infer<typeof MerkleCommitSchema>;\n\n// =============================================================================\n// Encryption envelope\n// =============================================================================\n//\n// The wire `enc` value is a CHOICE between two readings, mirroring the\n// grammar's `enc = enc-scheme-1 / enc-opaque`:\n//\n// - the scheme-1 shape — the closed map this revision defines, applied by\n// the validator only when `scheme`, `kem`, and `aead` are ALL supported\n// identifiers;\n// - the opaque reading — `scheme` (any CBOR unsigned integer) plus\n// arbitrary text-keyed bounded metadata, the degrade-to-opaque escape\n// hatch for envelopes under identifiers this implementation does not\n// support (`ENC_UNSUPPORTED`).\n//\n// The choice is not a discriminator: a `scheme: 1` envelope that fails the\n// scheme-1 shape is rejected with its typed code, never reclassified as\n// opaque. The validator therefore keeps `enc` as `unknown` at the item layer\n// and dispatches on raw `scheme` / `kem` / `aead` support before applying\n// `EncScheme1Schema`.\n\n// Per-slot recipient entry. The slot shape is KEM-driven:\n//\n// - x25519: `{ epk: bstr(32), wrap: bstr(48) }` — `epk` is the\n// ephemeral X25519 public key.\n// - mlkem768x25519: `{ kem_ct: bstr(1120), wrap: bstr(48) }` — `kem_ct` is\n// the single 1120-byte X-Wing encapsulation; there is NO per-slot `epk`\n// on the hybrid path (the X25519 ephemeral is the trailing 32 bytes of\n// `kem_ct`).\n//\n// The schema is deliberately PERMISSIVE: all three fields are optional and\n// `.strict()` is not applied, because the required shape depends on the\n// envelope-level `kem`, which a slot cannot see in isolation. The KEM-driven\n// gate in the validator emits the precise codes (`KEM_EPK_LENGTH_MISMATCH`,\n// `KEM_CT_LENGTH_MISMATCH`, `WRAP_LENGTH_MISMATCH`, `ENC_SLOT_INVALID_SHAPE`)\n// against the RAW decoded slot key set, so cross-KEM contamination and stray\n// keys are rejected even though the schema strips nothing here.\nexport const SlotSchema = textKeyedMap(\n z.object({\n epk: z.instanceof(Uint8Array).optional(),\n kem_ct: z.instanceof(Uint8Array).optional(),\n wrap: z.instanceof(Uint8Array).optional(),\n }),\n);\nexport type Slot = z.infer<typeof SlotSchema>;\n\n// Argon2id params `{m, t, p}` — a closed map of CBOR unsigned integers in the\n// pinned `0 .. 2^32 − 1` exact-integer range. The closed-map rule, the range,\n// and the floor checks all emit their codes in the validator's domain pass;\n// this schema is the public convenience type for producers.\nexport const Argon2idParamsSchema = textKeyedMap(\n z\n .object({\n m: z.union([z.number().int(), z.bigint()]),\n t: z.union([z.number().int(), z.bigint()]),\n p: z.union([z.number().int(), z.bigint()]),\n })\n .strict(),\n);\nexport type Argon2idParams = z.infer<typeof Argon2idParamsSchema>;\n\n// Passphrase block. `alg` is open (registry membership →\n// `ENC_PASSPHRASE_ALG_UNSUPPORTED` in the domain pass); `params` is open here\n// (the domain pass narrows on the registered `alg` and emits\n// `SCHEMA_UNKNOWN_FIELD` / `SCHEMA_MISSING_REQUIRED` / `SCHEMA_TYPE_MISMATCH`\n// / the floor and ceiling codes per sub-field). Salt length bounds are\n// schema-layer refinements carrying their dedicated codes.\nexport const PassphraseBlockSchema = textKeyedMap(\n z\n .object({\n alg: z.string(),\n salt: z.instanceof(Uint8Array).superRefine((bytes, ctx) => {\n if (bytes.length < 16) {\n ctx.addIssue({\n code: 'custom',\n path: [],\n message: `passphrase.salt length ${bytes.length} < 16`,\n params: { code: 'ENC_PASSPHRASE_SALT_TOO_SHORT' },\n });\n } else if (bytes.length > 64) {\n ctx.addIssue({\n code: 'custom',\n path: [],\n message: `passphrase.salt length ${bytes.length} > 64`,\n params: { code: 'ENC_PASSPHRASE_SALT_TOO_LONG' },\n });\n }\n }),\n params: z.record(z.string(), z.unknown()),\n })\n .strict(),\n);\nexport type PassphraseBlock = z.infer<typeof PassphraseBlockSchema>;\n\n// The typed scheme-1 envelope arm. Applied by the validator only when\n// `scheme` / `kem` / `aead` are all supported identifiers; under an\n// unsupported identifier the envelope takes the opaque reading instead and\n// this schema never runs. The map is CLOSED (`.strict()`): an unknown key in\n// a supported envelope is `SCHEMA_UNKNOWN_FIELD`, never a reason to fall back\n// to the opaque reading. Cross-field key-path invariants (exclusivity,\n// `slots` ↔ `slots_mac` ↔ `kem`, non-empty `slots`) are domain checks.\nexport const EncScheme1Schema = textKeyedMap(\n z\n .object({\n scheme: z.literal(1),\n aead: z.string(),\n kem: z.string().optional(),\n nonce: z.instanceof(Uint8Array),\n slots: z.array(SlotSchema).optional(),\n slots_mac: z\n .instanceof(Uint8Array)\n .refine((b) => b.length === 32, {\n params: { code: 'ENC_SLOTS_MAC_INVALID_LENGTH' },\n })\n .optional(),\n passphrase: PassphraseBlockSchema.optional(),\n })\n .strict(),\n);\nexport type EncScheme1 = z.infer<typeof EncScheme1Schema>;\n\n// The opaque reading: `scheme` is the only structurally required key (any\n// CBOR unsigned integer — `bigint` admits values above 2^53), and every other\n// text-keyed entry is unconstrained bounded metadata. A verifier escape\n// hatch, never a producer surface.\nexport const EncOpaqueSchema = textKeyedMap(\n z.looseObject({\n scheme: z.union([z.number().int().nonnegative(), z.bigint().nonnegative()]),\n }),\n);\nexport type EncOpaque = z.infer<typeof EncOpaqueSchema>;\n\nexport const EncryptionEnvelopeSchema = z.union([EncScheme1Schema, EncOpaqueSchema]);\nexport type EncryptionEnvelope = z.infer<typeof EncryptionEnvelopeSchema>;\n\n// =============================================================================\n// Item entry\n// =============================================================================\n\nexport const ItemEntrySchema = textKeyedMap(\n z\n .object({\n hashes: HashesMapSchema,\n uris: z.array(UriSchema).optional(),\n // Captured as `unknown`: the envelope is a union whose disposition\n // (typed scheme-1 vs opaque) depends on identifier support, so the\n // validator's domain pass — not this schema — narrows it.\n enc: z.unknown().optional(),\n })\n .strict(),\n);\nexport type ItemEntry = z.infer<typeof ItemEntrySchema>;\n\n// =============================================================================\n// Sig entry\n// =============================================================================\n//\n// Closed CBOR map `{cose_sign1, ? cose_key}`. Each value is a SINGLE byte\n// string carrying the CBOR-encoded COSE_Sign1 / COSE_Key. Canonical CBOR\n// map-key sort (RFC 8949 §4.2.1, bytewise lex on encoded keys) places\n// `cose_key` before `cose_sign1`; schema property order is irrelevant.\nexport const SigEntrySchema = textKeyedMap(\n z\n .object({\n cose_key: z.instanceof(Uint8Array).optional(),\n cose_sign1: z.instanceof(Uint8Array),\n })\n .strict(),\n);\nexport type SigEntry = z.infer<typeof SigEntrySchema>;\n\n// =============================================================================\n// Supersedence\n// =============================================================================\n\nexport const SupersedesSchema = z.instanceof(Uint8Array).refine((b) => b.length === 32, {\n params: { code: 'SUPERSEDES_TX_INVALID_LENGTH' },\n});\nexport type Supersedes = z.infer<typeof SupersedesSchema>;\n\n// =============================================================================\n// Top-level record\n// =============================================================================\n//\n// `v == 1` is a literal — a future major (`v: 2`) MUST be rejected with\n// `SCHEMA_INVALID_LITERAL`. `z.literal(1)` preserves the narrow `1` type for\n// the inferred `PoeRecord[\"v\"]` and emits Zod's `invalid_value` code, which\n// the validator's mapper lifts to `SCHEMA_INVALID_LITERAL`.\n//\n// `looseObject` admits extension keys; the validator's domain pass rejects\n// unknown keys that match neither extension namespace with\n// `SCHEMA_UNKNOWN_FIELD`.\nexport const VersionLiteralSchema = z.literal(1);\n\nexport const PoeRecordSchema = textKeyedMap(\n z.looseObject({\n v: VersionLiteralSchema,\n items: z.array(ItemEntrySchema).optional(),\n merkle: z.array(MerkleCommitSchema).optional(),\n supersedes: SupersedesSchema.optional(),\n sigs: z.array(SigEntrySchema).optional(),\n crit: z.array(z.string()).optional(),\n }),\n);\nexport type PoeRecord = z.infer<typeof PoeRecordSchema>;\n\n// =============================================================================\n// Closed top-level base-key registry\n// =============================================================================\n//\n// Used by the validator's domain pass to distinguish unknown-typo keys from\n// well-formed extension keys.\nexport const TOP_LEVEL_BASE_KEYS: ReadonlySet<string> = new Set([\n 'v',\n 'items',\n 'merkle',\n 'supersedes',\n 'sigs',\n 'crit',\n]);\n\n// Extension-key namespaces: `^x-.+` (vendor / experimental) and `^[a-z]+-.+`\n// (companion namespace), with control characters (U+0000–U+001F,\n// U+007F–U+009F) rejected ANYWHERE in the key — including a trailing newline,\n// so `x-note\\n` and `x-a\\nb` are both outside the namespace. The suffix\n// character class spells the exclusion out rather than relying on `.`\n// semantics, the literal `x-` / `[a-z]+-` prefixes admit no control\n// characters by construction, and `$` in JavaScript matches only at the true\n// end of the string, so a trailing newline cannot hide behind the anchor.\n// eslint-disable-next-line no-control-regex -- the control-character exclusion IS the admissibility rule\nexport const EXTENSION_KEY_VENDOR_RE = /^x-[^\\u0000-\\u001f\\u007f-\\u009f]+$/;\n// eslint-disable-next-line no-control-regex -- the control-character exclusion IS the admissibility rule\nexport const EXTENSION_KEY_COMPANION_RE = /^[a-z]+-[^\\u0000-\\u001f\\u007f-\\u009f]+$/;\n\nexport function isExtensionKey(k: string): boolean {\n return EXTENSION_KEY_VENDOR_RE.test(k) || EXTENSION_KEY_COMPANION_RE.test(k);\n}\n"]}