@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.
- package/dist/audit/composed.d.ts +18 -0
- package/dist/audit/composed.d.ts.map +1 -0
- package/dist/audit/composed.js +28 -0
- package/dist/audit/recorder.d.ts +10 -2
- package/dist/audit/recorder.d.ts.map +1 -1
- package/dist/capability/capability.d.ts +23 -0
- package/dist/capability/capability.d.ts.map +1 -1
- package/dist/capability/capability.js +2 -0
- package/dist/capability/purge.d.ts +122 -0
- package/dist/capability/purge.d.ts.map +1 -0
- package/dist/capability/purge.js +131 -0
- package/dist/cli/commandCatalog.generated.d.ts +54 -2
- package/dist/cli/commandCatalog.generated.d.ts.map +1 -1
- package/dist/cli/commandCatalog.generated.js +61 -0
- package/dist/cli/commandFacts.d.ts +1 -0
- package/dist/cli/commandFacts.d.ts.map +1 -1
- package/dist/cli/commandFacts.js +1 -0
- package/dist/controlPlane/capability.d.ts.map +1 -1
- package/dist/controlPlane/capability.js +7 -0
- package/dist/controlPlane/data/connection.d.ts +2 -12
- package/dist/controlPlane/data/connection.d.ts.map +1 -1
- package/dist/controlPlane/data/connection.js +1 -1
- package/dist/controlPlane/discovery/adminRoute.d.ts +29 -0
- package/dist/controlPlane/discovery/adminRoute.d.ts.map +1 -1
- package/dist/controlPlane/discovery/adminRoute.js +3 -0
- package/dist/controlPlane/error/errors.d.ts +4 -3
- package/dist/controlPlane/error/errors.d.ts.map +1 -1
- package/dist/controlPlane/http/guard.d.ts.map +1 -1
- package/dist/controlPlane/http/guard.js +40 -1
- package/dist/controlPlane/http/handlers.d.ts +79 -0
- package/dist/controlPlane/http/handlers.d.ts.map +1 -1
- package/dist/controlPlane/http/handlers.js +37 -3
- package/dist/controlPlane/http/responses.d.ts +98 -0
- package/dist/controlPlane/http/responses.d.ts.map +1 -1
- package/dist/controlPlane/http/responses.js +41 -1
- package/dist/controlPlane/http/routes.d.ts +1 -0
- package/dist/controlPlane/http/routes.d.ts.map +1 -1
- package/dist/controlPlane/http/routes.js +17 -3
- package/dist/controlPlane/scope/scope.d.ts +13 -3
- package/dist/controlPlane/scope/scope.d.ts.map +1 -1
- package/dist/controlPlane/scope/scope.js +14 -4
- package/dist/createBackend.d.ts.map +1 -1
- package/dist/createBackend.js +2 -0
- package/dist/createEntrypoint.d.ts +7 -1
- package/dist/createEntrypoint.d.ts.map +1 -1
- package/dist/createEntrypoint.js +15 -5
- package/dist/crypto/envelope.d.ts +86 -0
- package/dist/crypto/envelope.d.ts.map +1 -0
- package/dist/crypto/envelope.js +129 -0
- package/dist/crypto/fieldKeys.d.ts +41 -0
- package/dist/crypto/fieldKeys.d.ts.map +1 -0
- package/dist/crypto/fieldKeys.js +56 -0
- package/dist/data/codecs.d.ts +23 -10
- package/dist/data/codecs.d.ts.map +1 -1
- package/dist/data/codecs.js +50 -36
- package/dist/error/extend.d.ts +6 -5
- package/dist/error/extend.d.ts.map +1 -1
- package/dist/error/http.d.ts +72 -0
- package/dist/error/http.d.ts.map +1 -1
- package/dist/error/payload.d.ts +144 -0
- package/dist/error/payload.d.ts.map +1 -1
- package/dist/error/payload.js +48 -0
- package/dist/error/pithyError.d.ts +18 -7
- package/dist/error/pithyError.d.ts.map +1 -1
- package/dist/error/pithyError.js +6 -0
- package/dist/http/validation.d.ts +11 -2
- package/dist/http/validation.d.ts.map +1 -1
- package/dist/http/validation.js +20 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/logger/logger.d.ts +12 -5
- package/dist/logger/logger.d.ts.map +1 -1
- package/dist/logger/record.d.ts +42 -0
- package/dist/logger/record.d.ts.map +1 -1
- package/dist/privacy/blindIndex.d.ts +27 -0
- package/dist/privacy/blindIndex.d.ts.map +1 -0
- package/dist/privacy/blindIndex.js +57 -0
- package/dist/privacy/classes.d.ts +142 -0
- package/dist/privacy/classes.d.ts.map +1 -0
- package/dist/privacy/classes.js +209 -0
- package/dist/privacy/classify.d.ts +336 -0
- package/dist/privacy/classify.d.ts.map +1 -0
- package/dist/privacy/classify.js +1024 -0
- package/dist/privacy/encrypted.d.ts +210 -0
- package/dist/privacy/encrypted.d.ts.map +1 -0
- package/dist/privacy/encrypted.js +350 -0
- package/dist/privacy/encryptedColumns.d.ts +64 -0
- package/dist/privacy/encryptedColumns.d.ts.map +1 -0
- package/dist/privacy/encryptedColumns.js +134 -0
- package/dist/privacy/fieldRotation.d.ts +34 -0
- package/dist/privacy/fieldRotation.d.ts.map +1 -0
- package/dist/privacy/fieldRotation.js +186 -0
- package/dist/privacy/inventory.d.ts +12 -0
- package/dist/privacy/inventory.d.ts.map +1 -0
- package/dist/privacy/inventory.js +154 -0
- package/dist/privacy/posture.d.ts +114 -0
- package/dist/privacy/posture.d.ts.map +1 -0
- package/dist/privacy/posture.js +136 -0
- package/dist/privacy/redact.d.ts +7 -0
- package/dist/privacy/redact.d.ts.map +1 -0
- package/dist/privacy/redact.js +404 -0
- package/dist/privacy/report.d.ts +58 -0
- package/dist/privacy/report.d.ts.map +1 -0
- package/dist/privacy/report.js +15 -0
- package/dist/privacy/retirement.d.ts +68 -0
- package/dist/privacy/retirement.d.ts.map +1 -0
- package/dist/privacy/retirement.js +72 -0
- package/dist/privacy/sealed.d.ts +87 -0
- package/dist/privacy/sealed.d.ts.map +1 -0
- package/dist/privacy/sealed.js +221 -0
- package/dist/privacy/types.d.ts +559 -0
- package/dist/privacy/types.d.ts.map +1 -0
- package/dist/privacy/types.js +2 -0
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/package.json +1 -1
- package/src/audit/composed.ts +30 -0
- package/src/audit/recorder.ts +10 -2
- package/src/capability/capability.ts +26 -0
- package/src/capability/purge.ts +187 -0
- package/src/cli/commandCatalog.generated.ts +65 -0
- package/src/cli/commandFacts.ts +1 -0
- package/src/controlPlane/capability.ts +11 -0
- package/src/controlPlane/discovery/adminRoute.ts +11 -0
- package/src/controlPlane/error/errors.ts +4 -3
- package/src/controlPlane/http/guard.ts +60 -1
- package/src/controlPlane/http/handlers.ts +57 -4
- package/src/controlPlane/http/responses.ts +100 -0
- package/src/controlPlane/http/routes.ts +31 -1
- package/src/controlPlane/scope/scope.ts +14 -3
- package/src/controlPlane/token/base64url.ts +1 -1
- package/src/createBackend.ts +4 -0
- package/src/createEntrypoint.ts +17 -2
- package/src/crypto/envelope.ts +196 -0
- package/src/crypto/fieldKeys.ts +76 -0
- package/src/data/codecs.ts +69 -37
- package/src/error/extend.ts +6 -5
- package/src/error/payload.ts +110 -0
- package/src/error/pithyError.ts +21 -7
- package/src/http/validation.ts +35 -3
- package/src/index.ts +1 -0
- package/src/logger/logger.ts +12 -5
- package/src/privacy/blindIndex.ts +81 -0
- package/src/privacy/classes.ts +277 -0
- package/src/privacy/classify.ts +1447 -0
- package/src/privacy/encrypted.ts +513 -0
- package/src/privacy/encryptedColumns.ts +185 -0
- package/src/privacy/fieldRotation.ts +234 -0
- package/src/privacy/inventory.ts +147 -0
- package/src/privacy/posture.ts +205 -0
- package/src/privacy/redact.ts +423 -0
- package/src/privacy/report.ts +64 -0
- package/src/privacy/retirement.ts +102 -0
- package/src/privacy/sealed.ts +323 -0
- package/src/privacy/types.ts +804 -0
- 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":"AAGA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAI7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AAEH,oGAAoG;AACpG,MAAM,WAAW,YAAY;IAC3B,4GAA4G;IAC5G,IAAI,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kHAAkH;IAClH,WAAW,CAAC,EAAE,IAAI,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,QAAS,CAAC;AAEvC;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,QAAS,CAAC;AAEvC;;;;GAIG;AACH,eAAO,MAAM,cAAc,gBAAgB,CAAC;AAE5C;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAI,MAAM,EAAE,CAExC;AAED;;;GAGG;AACH,MAAM,WAAW,IAAI;IACnB,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAC3C,0GAA0G;IAC1G,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACvC,+CAA+C;IAC/C,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1C,yDAAyD;IACzD,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC1C,yEAAyE;IACzE,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;CACxC;AAED,sDAAsD;AACtD,wBAAgB,OAAO,IAAI,IAAI,CAS9B;AAwFD;;;GAGG;AACH,MAAM,MAAM,SAAS,GACjB,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,UAAU,GACV,MAAM,GACN,QAAQ,GACR,OAAO,GACP,KAAK,GACL,KAAK,CAAC;AAoCV,yGAAyG;AACzG,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAQjE;AA4DD,2FAA2F;AAC3F,UAAU,UAAU;IAClB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAyLD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,MAAM,GAAG,IAAI,CAY1D;AAwCD,4CAA4C;AAC5C,UAAU,MAAM;IACd,mCAAmC;IACnC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACtC,gFAAgF;IAChF,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAqED,sCAAsC;AACtC,MAAM,WAAW,IAAI;IACnB;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,2GAA2G;IAC3G,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,oHAAoH;AACpH,MAAM,WAAW,QAAQ;IACvB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAAC;IAChC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,IAAI,GAAG,SAAS,CAAC;IACpC,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1C;;;;;;;OAOG;IACH,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG;QAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAAA;KAAE,GAAG,SAAS,CAAC;IACxE,mHAAmH;IACnH,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACpC,gGAAgG;IAChG,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,yDAAyD;IACzD,KAAK,CAAC,GAAG,EAAE,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC9C,gDAAgD;IAChD,QAAQ,CAAC,IAAI,EAAE,SAAS,WAAW,EAAE,CAAC;IACtC,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC;IACxC,gDAAgD;IAChD,QAAQ,CAAC,GAAG,EAAE,QAAQ,GAAG,SAAS,CAAC;IACnC,8DAA8D;IAC9D,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC1C,mDAAmD;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4FAA4F;IAC5F,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,CAAC;IACpC,gDAAgD;IAChD,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC;IACxC,wCAAwC;IACxC,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,SAAS,CAAC;CACxC;AA2BD,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,GAAG,QAAQ,GAAG,SAAS,CAiB1F;AA+VD,mHAAmH;AACnH,wBAAgB,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,GAAE,IAAgB,GAAG,QAAQ,CAEhF;AAED,yDAAyD;AACzD,MAAM,WAAW,QAAQ;IACvB;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,gHAAgH;IAChH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC/D,WAAW,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/C;AAyHD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,SAAK,GAAG,IAAI,CAoDvF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,YAAY,EAAE,CAejE;AAED,8GAA8G;AAC9G,wBAAgB,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAExE"}
|