@pithy-sh/core 0.11.0 → 0.13.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 (157) hide show
  1. package/dist/audit/composed.d.ts +18 -0
  2. package/dist/audit/composed.d.ts.map +1 -0
  3. package/dist/audit/composed.js +28 -0
  4. package/dist/audit/recorder.d.ts +10 -2
  5. package/dist/audit/recorder.d.ts.map +1 -1
  6. package/dist/capability/capability.d.ts +23 -0
  7. package/dist/capability/capability.d.ts.map +1 -1
  8. package/dist/capability/capability.js +2 -0
  9. package/dist/capability/purge.d.ts +122 -0
  10. package/dist/capability/purge.d.ts.map +1 -0
  11. package/dist/capability/purge.js +131 -0
  12. package/dist/cli/commandCatalog.generated.d.ts +54 -2
  13. package/dist/cli/commandCatalog.generated.d.ts.map +1 -1
  14. package/dist/cli/commandCatalog.generated.js +61 -0
  15. package/dist/cli/commandFacts.d.ts +1 -0
  16. package/dist/cli/commandFacts.d.ts.map +1 -1
  17. package/dist/cli/commandFacts.js +1 -0
  18. package/dist/controlPlane/capability.d.ts.map +1 -1
  19. package/dist/controlPlane/capability.js +7 -0
  20. package/dist/controlPlane/data/connection.d.ts +2 -12
  21. package/dist/controlPlane/data/connection.d.ts.map +1 -1
  22. package/dist/controlPlane/data/connection.js +1 -1
  23. package/dist/controlPlane/discovery/adminRoute.d.ts +29 -0
  24. package/dist/controlPlane/discovery/adminRoute.d.ts.map +1 -1
  25. package/dist/controlPlane/discovery/adminRoute.js +3 -0
  26. package/dist/controlPlane/error/errors.d.ts +4 -3
  27. package/dist/controlPlane/error/errors.d.ts.map +1 -1
  28. package/dist/controlPlane/http/guard.d.ts.map +1 -1
  29. package/dist/controlPlane/http/guard.js +40 -1
  30. package/dist/controlPlane/http/handlers.d.ts +79 -0
  31. package/dist/controlPlane/http/handlers.d.ts.map +1 -1
  32. package/dist/controlPlane/http/handlers.js +37 -3
  33. package/dist/controlPlane/http/responses.d.ts +98 -0
  34. package/dist/controlPlane/http/responses.d.ts.map +1 -1
  35. package/dist/controlPlane/http/responses.js +41 -1
  36. package/dist/controlPlane/http/routes.d.ts +1 -0
  37. package/dist/controlPlane/http/routes.d.ts.map +1 -1
  38. package/dist/controlPlane/http/routes.js +17 -3
  39. package/dist/controlPlane/scope/scope.d.ts +13 -3
  40. package/dist/controlPlane/scope/scope.d.ts.map +1 -1
  41. package/dist/controlPlane/scope/scope.js +14 -4
  42. package/dist/createBackend.d.ts.map +1 -1
  43. package/dist/createBackend.js +2 -0
  44. package/dist/createEntrypoint.d.ts +7 -1
  45. package/dist/createEntrypoint.d.ts.map +1 -1
  46. package/dist/createEntrypoint.js +15 -5
  47. package/dist/crypto/envelope.d.ts +86 -0
  48. package/dist/crypto/envelope.d.ts.map +1 -0
  49. package/dist/crypto/envelope.js +129 -0
  50. package/dist/crypto/fieldKeys.d.ts +41 -0
  51. package/dist/crypto/fieldKeys.d.ts.map +1 -0
  52. package/dist/crypto/fieldKeys.js +56 -0
  53. package/dist/data/codecs.d.ts +23 -10
  54. package/dist/data/codecs.d.ts.map +1 -1
  55. package/dist/data/codecs.js +50 -36
  56. package/dist/error/extend.d.ts +6 -5
  57. package/dist/error/extend.d.ts.map +1 -1
  58. package/dist/error/http.d.ts +72 -0
  59. package/dist/error/http.d.ts.map +1 -1
  60. package/dist/error/payload.d.ts +144 -0
  61. package/dist/error/payload.d.ts.map +1 -1
  62. package/dist/error/payload.js +48 -0
  63. package/dist/error/pithyError.d.ts +18 -7
  64. package/dist/error/pithyError.d.ts.map +1 -1
  65. package/dist/error/pithyError.js +6 -0
  66. package/dist/http/validation.d.ts +11 -2
  67. package/dist/http/validation.d.ts.map +1 -1
  68. package/dist/http/validation.js +20 -3
  69. package/dist/index.d.ts +1 -1
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +2 -2
  72. package/dist/logger/logger.d.ts +12 -5
  73. package/dist/logger/logger.d.ts.map +1 -1
  74. package/dist/logger/record.d.ts +42 -0
  75. package/dist/logger/record.d.ts.map +1 -1
  76. package/dist/privacy/blindIndex.d.ts +27 -0
  77. package/dist/privacy/blindIndex.d.ts.map +1 -0
  78. package/dist/privacy/blindIndex.js +57 -0
  79. package/dist/privacy/classes.d.ts +142 -0
  80. package/dist/privacy/classes.d.ts.map +1 -0
  81. package/dist/privacy/classes.js +209 -0
  82. package/dist/privacy/classify.d.ts +336 -0
  83. package/dist/privacy/classify.d.ts.map +1 -0
  84. package/dist/privacy/classify.js +1024 -0
  85. package/dist/privacy/encrypted.d.ts +210 -0
  86. package/dist/privacy/encrypted.d.ts.map +1 -0
  87. package/dist/privacy/encrypted.js +350 -0
  88. package/dist/privacy/encryptedColumns.d.ts +64 -0
  89. package/dist/privacy/encryptedColumns.d.ts.map +1 -0
  90. package/dist/privacy/encryptedColumns.js +134 -0
  91. package/dist/privacy/fieldRotation.d.ts +34 -0
  92. package/dist/privacy/fieldRotation.d.ts.map +1 -0
  93. package/dist/privacy/fieldRotation.js +186 -0
  94. package/dist/privacy/inventory.d.ts +12 -0
  95. package/dist/privacy/inventory.d.ts.map +1 -0
  96. package/dist/privacy/inventory.js +154 -0
  97. package/dist/privacy/posture.d.ts +114 -0
  98. package/dist/privacy/posture.d.ts.map +1 -0
  99. package/dist/privacy/posture.js +136 -0
  100. package/dist/privacy/redact.d.ts +7 -0
  101. package/dist/privacy/redact.d.ts.map +1 -0
  102. package/dist/privacy/redact.js +404 -0
  103. package/dist/privacy/report.d.ts +58 -0
  104. package/dist/privacy/report.d.ts.map +1 -0
  105. package/dist/privacy/report.js +15 -0
  106. package/dist/privacy/retirement.d.ts +68 -0
  107. package/dist/privacy/retirement.d.ts.map +1 -0
  108. package/dist/privacy/retirement.js +72 -0
  109. package/dist/privacy/sealed.d.ts +87 -0
  110. package/dist/privacy/sealed.d.ts.map +1 -0
  111. package/dist/privacy/sealed.js +221 -0
  112. package/dist/privacy/types.d.ts +559 -0
  113. package/dist/privacy/types.d.ts.map +1 -0
  114. package/dist/privacy/types.js +2 -0
  115. package/dist/version.generated.d.ts +1 -1
  116. package/dist/version.generated.js +1 -1
  117. package/package.json +1 -1
  118. package/src/audit/composed.ts +30 -0
  119. package/src/audit/recorder.ts +10 -2
  120. package/src/capability/capability.ts +26 -0
  121. package/src/capability/purge.ts +187 -0
  122. package/src/cli/commandCatalog.generated.ts +65 -0
  123. package/src/cli/commandFacts.ts +1 -0
  124. package/src/controlPlane/capability.ts +11 -0
  125. package/src/controlPlane/discovery/adminRoute.ts +11 -0
  126. package/src/controlPlane/error/errors.ts +4 -3
  127. package/src/controlPlane/http/guard.ts +60 -1
  128. package/src/controlPlane/http/handlers.ts +57 -4
  129. package/src/controlPlane/http/responses.ts +100 -0
  130. package/src/controlPlane/http/routes.ts +31 -1
  131. package/src/controlPlane/scope/scope.ts +14 -3
  132. package/src/controlPlane/token/base64url.ts +1 -1
  133. package/src/createBackend.ts +4 -0
  134. package/src/createEntrypoint.ts +17 -2
  135. package/src/crypto/envelope.ts +196 -0
  136. package/src/crypto/fieldKeys.ts +76 -0
  137. package/src/data/codecs.ts +69 -37
  138. package/src/error/extend.ts +6 -5
  139. package/src/error/payload.ts +110 -0
  140. package/src/error/pithyError.ts +21 -7
  141. package/src/http/validation.ts +35 -3
  142. package/src/index.ts +1 -0
  143. package/src/logger/logger.ts +12 -5
  144. package/src/privacy/blindIndex.ts +81 -0
  145. package/src/privacy/classes.ts +277 -0
  146. package/src/privacy/classify.ts +1447 -0
  147. package/src/privacy/encrypted.ts +513 -0
  148. package/src/privacy/encryptedColumns.ts +185 -0
  149. package/src/privacy/fieldRotation.ts +234 -0
  150. package/src/privacy/inventory.ts +147 -0
  151. package/src/privacy/posture.ts +205 -0
  152. package/src/privacy/redact.ts +423 -0
  153. package/src/privacy/report.ts +64 -0
  154. package/src/privacy/retirement.ts +102 -0
  155. package/src/privacy/sealed.ts +323 -0
  156. package/src/privacy/types.ts +804 -0
  157. package/src/version.generated.ts +1 -1
@@ -0,0 +1,336 @@
1
+ // SPDX-FileCopyrightText: 2026 Pithy
2
+ // SPDX-License-Identifier: MIT
3
+ import type { z } from "zod";
4
+ /**
5
+ * **Reading a privacy marker off a schema, and every marker off a schema tree** (#132).
6
+ *
7
+ * The marker is `meta({ privacy })` on the schema a marker was handed. {@link privacyOf} reads it through the
8
+ * wrappers that are the same field — `pii(z.string()).optional()` is `pii` — and {@link classifyPrivacy} names
9
+ * each position of a schema that `redact` masks.
10
+ *
11
+ * ## The redaction contract
12
+ *
13
+ * `redact`, `classifyPrivacy` and `Redacted<T>` all read the one record this module builds for a position, so they
14
+ * agree on every one. The contract they keep is stated on #132 and in `docs/PRIVACY.md`:
15
+ *
16
+ * - **Walked exactly**: objects (strict, loose, catchall, and `pick`, `omit`, `partial`, `extend` of one), arrays,
17
+ * tuples and their rest, records, `Map` and `Set`; the wrappers `optional`, `nullable`, `default`, `catch` with a
18
+ * constant value, `readonly` and a brand; unions, discriminated unions and intersections, where an option's marks apply where
19
+ * the value has that option's shape — a marked string option to a string, never to an array or a number beside
20
+ * it — and a mark reached through no option applies to every value; the kit's `SQLiteDate`, `SQLiteBoolean` and
21
+ * `sqliteJson(schema)`, read through to the schema they decode to; and recursion, a `z.lazy` or a getter, to the
22
+ * one limit below.
23
+ * - **Everything else is opaque**: a transform, a pipe, a preprocess, any other codec, `z.custom`, `any`, `unknown`,
24
+ * and an `.overwrite()` or a `.catch()` with a fallback function over a structure, which make a new value.
25
+ * Nothing inside one is walked. With a mark anywhere beneath it, its position is masked whole with the strongest
26
+ * class it carries; with none, it is kept, a union option beside a marked one included.
27
+ * - **A mark on a container masks it whole**, and the container carries every class marked inside it as well as its
28
+ * own: `pii(z.object({ token: secret(z.string()) }))` is listed `pii` and `secret`, and masked `[secret]`.
29
+ * - **Nesting is bounded once**, at {@link MAX_RECURSION_DEPTH} levels: a position deeper than that is masked whole as
30
+ * {@link TRUNCATED_MASK}, whatever nests it, a recursion or a schema written out level by level. It is a safety
31
+ * cutoff rather than a privacy class. A recursion that repeats a position (the same set of schemas) already read on
32
+ * its path needs no cut in the list: its classes are those of the position already read. Recursions defined once
33
+ * side by side in a union, with cycles of different lengths, can reach the limit before any position repeats. Where
34
+ * a read reaches the limit on a chain of positions not yet seen on its path, a factory or plain nesting, or such a
35
+ * union, nothing past it was read, so the list assumes every class there, each `truncated`, as past the budget, and
36
+ * `hasPrivacy` answers yes for each: the list never says less than is marked. A path that reaches the limit ends
37
+ * alone: every other path is still read, so a class beside a recursion is found whatever the field order, and a
38
+ * position masked whole for a class it carries is masked with that class, cut off below or not. The cutoff is a flag
39
+ * beside the classes, never instead of them. Every answer is a function of the schema and the value alone.
40
+ * - **One budget per call, {@link MAX_READ_SCHEMAS} schemas.** `redact`, `classifyPrivacy`, `hasPrivacy` and
41
+ * `walkPrivacy` each read at most that many in one call, and every position and every search in it draws on it: each
42
+ * schema a read meets counts one, and so does each schema a lazy or an object's shape yields, where a getter runs and
43
+ * may build one. So a call's time and memory grow linearly with the size of the schema and the value, and are bounded
44
+ * beyond that by the budget and by what the schema's own getters build. Reads go shallowest first, so a class above the point the budget runs out on is found and
45
+ * kept. Past it the call fails closed: `redact` masks each position it has not resolved whole, with the strongest
46
+ * class it found there or {@link TRUNCATED_MASK} where it found none, and the list names every class at each position
47
+ * it has not read, each `truncated`: nothing there was read, so any class may be marked there, and the list never
48
+ * says less than the schema marks. It is one of five places the list says more than `redact` masks, beside a position
49
+ * cut at the depth limit, the classes a cut assumes, a reference's own path and an intersection that narrows. A position read once is read once
50
+ * wherever it sits in the call, so a schema reused in many fields costs one, and each call starts with the whole
51
+ * budget and keeps nothing for the next, so every answer is a function of the schema and the value alone. A position
52
+ * the walk cannot read at all, nested deeper than the stack can follow, is listed with every class, each `truncated`:
53
+ * nothing here throws.
54
+ * - **`classifyPrivacy` writes out at most {@link MAX_LISTED_PATHS} paths.** A schema reused in two fields at every
55
+ * level has a path per field per level, two to the depth; past the cap a position met again is listed at its own path
56
+ * once per class marked beneath it, with `sameAs` the path it was first written out under: `redact` masks the paths
57
+ * below it, not that path itself. So the paths it writes out are bounded too, however many reach one schema, and
58
+ * every class is still listed.
59
+ * - **An intersection is not narrowed.** Where one side narrows a field so it can never hold the type the other side
60
+ * marks, `classifyPrivacy` may still list that field: the inventory can list more than `redact` masks, and never
61
+ * less. `z.intersection(z.object({ b: z.number() }), z.object({ b: z.union([pii(z.string()), z.number()]) }))`
62
+ * lists `b` as `pii`, and `redact` keeps a `b` of `3`.
63
+ *
64
+ * ## What a path looks like
65
+ *
66
+ * Object keys join with `.`, an array or set element is `[]`, a tuple position is `[i]`, a record's or a map's value
67
+ * and an object's catchall are `*`, and a record's or a map's key is `<key>`: `address.line1`, `devices[].token`,
68
+ * `pair[1]`, `notes.*`, `byEmail.<key>`. A marked root is the empty path. A position masked whole is listed once per
69
+ * class it carries, and nothing below it is.
70
+ */
71
+ /** One marked path in a schema, and its class. The per-field record the inventory is built from. */
72
+ export interface PrivacyField {
73
+ /** Where in the value the field sits: `email`, `address.line1`, `devices[].token`, or `""` for the root. */
74
+ path: string;
75
+ /** The class it was marked with. */
76
+ class: string;
77
+ /**
78
+ * Set where the class is assumed rather than found: a position the call ran out of {@link MAX_READ_SCHEMAS} before it
79
+ * read, or a read beneath a position that ran out of it, a position cut at {@link MAX_RECURSION_DEPTH} on a chain of
80
+ * schemas not yet seen on its path, or a schema the walk could not read at all, nested deeper than the stack. Each
81
+ * lists {@link unreadClasses}. Absent everywhere the schema was read to the end.
82
+ */
83
+ truncated?: true;
84
+ /**
85
+ * Set on a reference: the path this field's position was first listed under. Once the walk has written out
86
+ * {@link MAX_LISTED_PATHS} paths, a position it meets again is listed once per class marked beneath it, with the path
87
+ * it was first written out under, rather than once per path below it. The paths below `sameAs` are the paths below
88
+ * this one.
89
+ */
90
+ sameAs?: string;
91
+ /**
92
+ * Whether the field rests encrypted in its store (#390): `true` for a field marked `encrypted()`, `false` for one
93
+ * that is not. Set by the inventory, on the observed side of #132's attestation split, and **absent where the core
94
+ * cannot say** — an assumed class, a reference, or a core older than #390 — never defaulted to `false`.
95
+ */
96
+ encryptedAtRest?: boolean;
97
+ /** Set on an encrypted field whose storage is not D1: nothing can prove it re-sealed, so it blocks retirement. */
98
+ unrotatable?: true;
99
+ }
100
+ /**
101
+ * **The one recursion limit**, shared by `redact`, `classifyPrivacy` and `Redacted<T>` (`./types` reads this constant's
102
+ * type). Depth is counted in levels of nesting: every object, array, tuple, record, map and set a position sits inside.
103
+ * It applies to all nesting, not only recursion: a position deeper than this is masked whole as {@link TRUNCATED_MASK}
104
+ * and typed `unknown`, whatever nests it: a `z.lazy`, a getter that returns one schema, one that builds a new schema
105
+ * each read, or a schema written out level by level. The list assumes every class there, unless the chain repeats a
106
+ * position (the same set of schemas) already read on its path. A search for a mark beneath an opaque node or a marked
107
+ * container ends a path at the same depth, counting each lazy too, and reads every other path; so does the one
108
+ * `Redacted<T>` makes. So does a read of the shapes a choice can have, and of the schemas that reach one position, each
109
+ * counting the lazies nested above a schema: a lazy that builds a new choice each read never meets a schema twice. A
110
+ * union of many lazies, none inside another, counts one.
111
+ */
112
+ export declare const MAX_RECURSION_DEPTH = 256;
113
+ /**
114
+ * **The one budget**: how many schemas one call of `redact`, `classifyPrivacy`, `hasPrivacy` or `walkPrivacy` reads.
115
+ * Every position and every search in that call draws on it. Each schema a read meets counts one — a step of the read of
116
+ * a position, of the shapes a choice can have, or of a search beneath a marked container or an opaque node — and so
117
+ * does each schema a lazy or an object's shape yields: a lazy's target, and each field of a shape, where a getter runs
118
+ * and may build a new schema. A schema met again in the same read costs nothing. So a call reads at most this many
119
+ * schemas, and runs at most this many of the schema's own getters beyond the fields of the one object it was reading
120
+ * when the budget ran out. So its time and memory grow linearly with the size of the schema and the value, and are
121
+ * bounded beyond that by the budget and by what one of those getters builds. Past it the call fails closed: what it has not read, it assumes. `redact` masks each position it has
122
+ * not resolved whole, with the strongest class it found there or {@link TRUNCATED_MASK} where it found none; the list
123
+ * names every class at each position it has not read, each `truncated`. Far past any schema written out by hand, and
124
+ * past any recursion defined once: only a schema that builds new ones as it is read, or one wider than any written out,
125
+ * reaches it. Each call starts with the whole budget and keeps nothing for the next, so every answer is a function of
126
+ * the schema and the value alone, whichever call came first.
127
+ */
128
+ export declare const MAX_READ_SCHEMAS = 10000;
129
+ /**
130
+ * How many paths {@link walkPrivacy} writes out before a position it met already is listed by reference. A schema
131
+ * reused in two fields at every level of thirty has thirty-one schemas and a billion paths, so a list of every path
132
+ * cannot be built; past this many, each new position is still read and written out once, and each one met again is
133
+ * listed at its own path with every class beneath it and `sameAs` the path it was written out under. So the paths it
134
+ * writes out are bounded too, however many reach one schema. Far past any table written out: a book of 22 groups of 22
135
+ * contacts of 22 addresses is written out whole.
136
+ */
137
+ export declare const MAX_LISTED_PATHS = 50000;
138
+ /**
139
+ * What `redact` puts where it stops: a value nested past {@link MAX_RECURSION_DEPTH}, an opaque value over a recursion
140
+ * that reaches it, and a position the call ran out of {@link MAX_READ_SCHEMAS} before it read, where no class was
141
+ * found. A safety cutoff rather than a privacy class, so it is never a placeholder for one.
142
+ */
143
+ export declare const TRUNCATED_MASK = "[truncated]";
144
+ /**
145
+ * The classes {@link classifyPrivacy} lists for a position it did not read: every class registered, strongest first,
146
+ * because nothing there was read and any of them may be marked below it. So the list never says less than the schema
147
+ * marks, and `hasPrivacy` answers yes for each. A read that runs out of {@link MAX_READ_SCHEMAS} assumes them where it
148
+ * stopped, a cut at {@link MAX_RECURSION_DEPTH} on a chain of schemas not yet seen assumes them where it cut, and a
149
+ * schema the walk cannot read at all is listed with them.
150
+ */
151
+ export declare function unreadClasses(): string[];
152
+ /**
153
+ * One call of `redact`, `classifyPrivacy`, `hasPrivacy` or `walkPrivacy`: how much of {@link MAX_READ_SCHEMAS} it has
154
+ * left, and what it has read, so a schema met again in the call is read once. Made new for each call and kept by none.
155
+ */
156
+ export interface Call {
157
+ /** How many more schemas the call may read. */
158
+ left: number;
159
+ /** The shapes each schema's output can have, read to its end. */
160
+ readonly kinds: Map<z.ZodType, KindsFound>;
161
+ /** The fewest lazies above each schema from which a read of its shapes is known to run past the limit. */
162
+ readonly stops: Map<z.ZodType, number>;
163
+ /** What a search found beneath each schema. */
164
+ readonly searched: Map<z.ZodType, Search>;
165
+ /** Every position read, by the schemas that reach it. */
166
+ readonly positions: Map<string, Position>;
167
+ /** Every position walked into, by its structural nodes and its masks. */
168
+ readonly walked: Map<string, Position>;
169
+ }
170
+ /** A call with the whole budget, and nothing read. */
171
+ export declare function newCall(): Call;
172
+ /**
173
+ * The shapes a value can have, as far as a choice is concerned: what `typeof` says, with an object told apart by what
174
+ * it is. An option's marks apply to a value of a shape the option names, and to no other.
175
+ */
176
+ export type ValueKind = "string" | "number" | "bigint" | "boolean" | "symbol" | "function" | "date" | "object" | "array" | "map" | "set";
177
+ /** The shape of a value, or `undefined` for `null` and `undefined`, which are kept wherever they are. */
178
+ export declare function kindOfValue(value: unknown): ValueKind | undefined;
179
+ /** A schema's shapes, read to its end, and how many lazies deep the read went below it. */
180
+ interface KindsFound {
181
+ readonly kinds: ReadonlySet<ValueKind>;
182
+ readonly reach: number;
183
+ }
184
+ /**
185
+ * The class a field was marked with, read through its wrappers — `pii(z.string()).optional()` is `pii` — or
186
+ * `null` when nothing along the chain carries one. A field marked twice, `pii(secret(x))`, is its strongest class.
187
+ * It never throws: a schema whose metadata cannot be read, cloned by `.describe()` or `.meta()` more times than Zod
188
+ * can read it back through, may carry any class, so it answers the strongest of {@link unreadClasses}.
189
+ */
190
+ export declare function privacyOf(schema: z.ZodType): string | null;
191
+ /** What a search found beneath a schema. */
192
+ interface Search {
193
+ /** Every class it found marked. */
194
+ readonly classes: ReadonlySet<string>;
195
+ /** Whether a path ended at the recursion limit, or the search at the budget. */
196
+ readonly truncated: boolean;
197
+ /**
198
+ * Whether every class is assumed beneath it: the search ended at the call's {@link MAX_READ_SCHEMAS}, or a path of
199
+ * schemas it had not met reached {@link MAX_RECURSION_DEPTH}. Either way, what lies past it was never read.
200
+ */
201
+ readonly unread: boolean;
202
+ }
203
+ /** How a position is masked whole. */
204
+ export interface Mask {
205
+ /**
206
+ * The strongest class found at the position: its placeholder is the mask, unless `own`. Never a class only assumed.
207
+ * Empty where none was found — a recursion that reached the limit, or a read that ran out of
208
+ * {@link MAX_READ_SCHEMAS}, is all there is — and only there is the mask {@link TRUNCATED_MASK}.
209
+ */
210
+ readonly className: string;
211
+ /** Whether the class's own mask applies: one class, from a marker, nothing opaque, and nothing cut off. */
212
+ readonly own: boolean;
213
+ /**
214
+ * Every class marked at the position, in the order met, then every class a read that ran out of
215
+ * {@link MAX_READ_SCHEMAS}, or reached {@link MAX_RECURSION_DEPTH} on a chain of schemas not yet seen, assumed. Never
216
+ * the cutoff, which is not a class.
217
+ */
218
+ readonly classes: readonly string[];
219
+ /**
220
+ * The classes in `classes` that no read found, only assumed: a read here ran out of {@link MAX_READ_SCHEMAS}, or
221
+ * reached {@link MAX_RECURSION_DEPTH} on a chain of schemas not yet seen, so any class may be marked past it. Listed
222
+ * `truncated`.
223
+ */
224
+ readonly assumed: readonly string[];
225
+ /**
226
+ * Whether a recursion here reached {@link MAX_RECURSION_DEPTH}, or a read ran out of {@link MAX_READ_SCHEMAS}: a flag
227
+ * beside the classes, never instead of them. A class found here is still the mask.
228
+ */
229
+ readonly truncated: boolean;
230
+ }
231
+ /** One position: every schema that reaches it, flattened through wrappers, choices, lazies and the kit's codecs. */
232
+ export interface Position {
233
+ /**
234
+ * Set when some value here is masked whole: every class the position masks, whatever the value's shape. What
235
+ * `classifyPrivacy` lists.
236
+ */
237
+ readonly mask: Mask | undefined;
238
+ /**
239
+ * How an object's string key is masked when it is read here. A string key is a string whatever the key schema says,
240
+ * a number key's included, so every mark applies to it but one that only a symbol option reaches.
241
+ */
242
+ readonly nameMask: Mask | undefined;
243
+ /** Whether every value here is masked whole, whatever its shape. */
244
+ readonly whole: boolean;
245
+ /**
246
+ * How a value is masked here, by its shape: a mark reached through a union option applies to a value of that
247
+ * option's shape, and one reached through no option to every value. `undefined` where the value is walked or kept.
248
+ */
249
+ maskFor(value: unknown): Mask | undefined;
250
+ /**
251
+ * Whether a value's kind and prototype miss what the schemas here expect, and how it is masked where they do: a
252
+ * class instance or a plain object where none of its kind is named, a `Date`, an array, a `Map` or a `Set` of a
253
+ * subclass or with fields where an object is, an array-like instance where an array is. `undefined` where it fits:
254
+ * a schema here names its kind and it is typical of that kind, or only an opaque node here takes its kind. A misfit
255
+ * is never walked. Its mask is the strongest class beneath the schemas here of its kind, or beneath every schema here
256
+ * where none names it, and `undefined`, to keep it as it is, where nothing there is marked.
257
+ */
258
+ misfit(value: unknown): {
259
+ readonly mask: Mask | undefined;
260
+ } | undefined;
261
+ /** The structural kinds here — `object`, `array`, `map` … — a value is walked by: none of a shape masked whole. */
262
+ readonly kinds: ReadonlySet<string>;
263
+ /** Whether anything here but a structural node may hold the value: a scalar, an opaque node. */
264
+ readonly other: boolean;
265
+ /** The position at an object key or a record's value. */
266
+ field(key: PropertyKey): Position | undefined;
267
+ /** The keys the objects here name, in order. */
268
+ readonly keys: readonly PropertyKey[];
269
+ /** The position at a key no object names: a catchall, a record's value. */
270
+ readonly catchall: Position | undefined;
271
+ /** The position a record's keys are read at. */
272
+ readonly key: Position | undefined;
273
+ /** The position at element `index` of an array or a tuple. */
274
+ item(index: number): Position | undefined;
275
+ /** How many items the longest tuple here names. */
276
+ readonly items: number;
277
+ /** The position at an element past every tuple item: an array's element, a tuple's rest. */
278
+ readonly rest: Position | undefined;
279
+ /** The positions of a map's keys and values. */
280
+ readonly mapKey: Position | undefined;
281
+ readonly mapValue: Position | undefined;
282
+ /** The position of a set's elements. */
283
+ readonly element: Position | undefined;
284
+ }
285
+ /** The position `schemas` reach, read once in the call for that list of schemas. */
286
+ export declare function positionOf(schemas: readonly z.ZodType[], call: Call): Position | undefined;
287
+ /** The position at the root of a schema, read in `call`: a new one, with the whole budget, unless one is given. */
288
+ export declare function rootPosition(schema: z.ZodType, call?: Call): Position;
289
+ /** What a walk says beside one masked path and class. */
290
+ export interface MarkNote {
291
+ /**
292
+ * Set where the class is assumed rather than found: a read ran out of its budget there, or the schema could not be
293
+ * read at all, so any class may be marked below it.
294
+ */
295
+ readonly truncated?: true;
296
+ /**
297
+ * Set on a reference, once {@link MAX_LISTED_PATHS} paths are written out: the path this position was first listed
298
+ * under. The class is marked somewhere beneath both, and the paths beneath this one are those beneath that one.
299
+ */
300
+ readonly sameAs?: string;
301
+ }
302
+ /**
303
+ * What a walk reports. `marked` is called once per masked path and class. `unmarkedKey`, when given, is called for each
304
+ * object key whose position is not masked whole, before the walk steps into it — the hook `pithy privacy scan` reads a
305
+ * field's name through, so the scan and the inventory walk one tree the same way.
306
+ */
307
+ export interface PrivacyVisitor {
308
+ /** One masked path and class, with a {@link MarkNote} where the class is assumed or the path is a reference. */
309
+ marked(path: string, className: string, note?: MarkNote): void;
310
+ unmarkedKey?(path: string, key: string): void;
311
+ }
312
+ /**
313
+ * Walk a schema tree the way {@link classifyPrivacy} does, from `path`, and report to `visitor`. Exported so a second
314
+ * reader of the tree — the CLI's scan — cannot walk it differently. A position met again below itself is a recursion
315
+ * defined once, and is not walked again: its fields are listed once. A position nested more than
316
+ * {@link MAX_RECURSION_DEPTH} levels deep is cut off by `redact` as {@link TRUNCATED_MASK}; where it is a position not
317
+ * met on its path, nothing past it was read, so it is listed with every class, each truncated.
318
+ *
319
+ * Positions are read level by level, shallowest first, and reported in the schema's own order, all in one call with one
320
+ * {@link MAX_READ_SCHEMAS}. Each distinct position is read once, wherever it sits; every level above the one the budget
321
+ * runs out on is read whole, and each position it did not read is listed with {@link unreadClasses}, each truncated.
322
+ * Once {@link MAX_LISTED_PATHS} paths are written out, a position met again is a reference: its path is listed with
323
+ * every class beneath it and the path it was first written out under, and nothing below it is written out again. So the
324
+ * walk is bounded by the budget, however many paths reach one schema. A schema that cannot be read at all, nested
325
+ * deeper than the stack or behind a getter that throws, is listed with every class, each truncated. It never throws.
326
+ */
327
+ export declare function walkPrivacy(schema: z.ZodType, visitor: PrivacyVisitor, path?: string): void;
328
+ /**
329
+ * Every position `redact` masks in a schema, and its class — the Art. 30 record for one object, derived rather than
330
+ * maintained. Empty for a schema with no markers. A path reached twice is listed once per class. It never throws.
331
+ */
332
+ export declare function classifyPrivacy(schema: z.ZodType): PrivacyField[];
333
+ /** Whether a schema holds a field of `className` at any depth: exactly what {@link classifyPrivacy} lists. */
334
+ export declare function hasPrivacy(schema: z.ZodType, className: string): boolean;
335
+ export {};
336
+ //# sourceMappingURL=classify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../../src/privacy/classify.ts"],"names":[],"mappings":"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"}