@blamejs/core 0.8.43 → 0.8.49

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 (222) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/README.md +10 -10
  3. package/index.js +52 -0
  4. package/lib/a2a.js +159 -34
  5. package/lib/acme.js +762 -0
  6. package/lib/ai-pref.js +166 -43
  7. package/lib/api-key.js +108 -47
  8. package/lib/api-snapshot.js +157 -40
  9. package/lib/app-shutdown.js +113 -77
  10. package/lib/archive.js +337 -40
  11. package/lib/arg-parser.js +697 -0
  12. package/lib/asyncapi.js +99 -55
  13. package/lib/atomic-file.js +465 -104
  14. package/lib/audit-chain.js +123 -34
  15. package/lib/audit-daily-review.js +389 -0
  16. package/lib/audit-sign.js +302 -56
  17. package/lib/audit-tools.js +412 -63
  18. package/lib/audit.js +656 -35
  19. package/lib/auth/jwt-external.js +17 -0
  20. package/lib/auth/oauth.js +7 -0
  21. package/lib/auth-bot-challenge.js +505 -0
  22. package/lib/auth-header.js +92 -25
  23. package/lib/backup/bundle.js +26 -0
  24. package/lib/backup/index.js +512 -89
  25. package/lib/backup/manifest.js +168 -7
  26. package/lib/break-glass.js +415 -39
  27. package/lib/budr.js +103 -30
  28. package/lib/bundler.js +86 -66
  29. package/lib/cache.js +192 -72
  30. package/lib/chain-writer.js +65 -40
  31. package/lib/circuit-breaker.js +56 -33
  32. package/lib/cli-helpers.js +106 -75
  33. package/lib/cli.js +6 -30
  34. package/lib/cloud-events.js +99 -32
  35. package/lib/cluster-storage.js +162 -37
  36. package/lib/cluster.js +340 -49
  37. package/lib/codepoint-class.js +66 -0
  38. package/lib/compliance.js +424 -24
  39. package/lib/config-drift.js +111 -46
  40. package/lib/config.js +94 -40
  41. package/lib/consent.js +165 -18
  42. package/lib/constants.js +1 -0
  43. package/lib/content-credentials.js +153 -48
  44. package/lib/cookies.js +154 -62
  45. package/lib/credential-hash.js +133 -61
  46. package/lib/crypto-field.js +702 -18
  47. package/lib/crypto-hpke.js +256 -0
  48. package/lib/crypto.js +744 -22
  49. package/lib/csv.js +178 -35
  50. package/lib/daemon.js +456 -0
  51. package/lib/dark-patterns.js +186 -55
  52. package/lib/db-query.js +79 -2
  53. package/lib/db.js +1431 -60
  54. package/lib/ddl-change-control.js +523 -0
  55. package/lib/deprecate.js +195 -40
  56. package/lib/dev.js +82 -39
  57. package/lib/dora.js +67 -48
  58. package/lib/dr-runbook.js +368 -0
  59. package/lib/dsr.js +142 -11
  60. package/lib/dual-control.js +91 -56
  61. package/lib/events.js +120 -41
  62. package/lib/external-db-migrate.js +192 -2
  63. package/lib/external-db.js +795 -50
  64. package/lib/fapi2.js +122 -1
  65. package/lib/fda-21cfr11.js +395 -0
  66. package/lib/fdx.js +132 -2
  67. package/lib/file-type.js +87 -0
  68. package/lib/file-upload.js +93 -0
  69. package/lib/flag.js +82 -20
  70. package/lib/forms.js +132 -29
  71. package/lib/framework-error.js +169 -0
  72. package/lib/framework-schema.js +163 -35
  73. package/lib/gate-contract.js +849 -175
  74. package/lib/graphql-federation.js +68 -7
  75. package/lib/guard-all.js +172 -55
  76. package/lib/guard-archive.js +286 -124
  77. package/lib/guard-auth.js +194 -21
  78. package/lib/guard-cidr.js +190 -28
  79. package/lib/guard-csv.js +397 -51
  80. package/lib/guard-domain.js +213 -91
  81. package/lib/guard-email.js +236 -29
  82. package/lib/guard-filename.js +307 -75
  83. package/lib/guard-graphql.js +263 -30
  84. package/lib/guard-html.js +310 -116
  85. package/lib/guard-image.js +243 -30
  86. package/lib/guard-json.js +260 -54
  87. package/lib/guard-jsonpath.js +235 -23
  88. package/lib/guard-jwt.js +284 -30
  89. package/lib/guard-markdown.js +204 -22
  90. package/lib/guard-mime.js +190 -26
  91. package/lib/guard-oauth.js +277 -28
  92. package/lib/guard-pdf.js +251 -27
  93. package/lib/guard-regex.js +226 -18
  94. package/lib/guard-shell.js +229 -26
  95. package/lib/guard-svg.js +177 -10
  96. package/lib/guard-template.js +232 -21
  97. package/lib/guard-time.js +195 -29
  98. package/lib/guard-uuid.js +189 -30
  99. package/lib/guard-xml.js +259 -36
  100. package/lib/guard-yaml.js +241 -44
  101. package/lib/honeytoken.js +63 -27
  102. package/lib/html-balance.js +83 -0
  103. package/lib/http-client.js +486 -59
  104. package/lib/http-message-signature.js +582 -0
  105. package/lib/i18n.js +102 -49
  106. package/lib/iab-mspa.js +112 -32
  107. package/lib/iab-tcf.js +107 -2
  108. package/lib/inbox.js +90 -52
  109. package/lib/keychain.js +865 -0
  110. package/lib/legal-hold.js +374 -0
  111. package/lib/local-db-thin.js +320 -0
  112. package/lib/log-stream.js +281 -51
  113. package/lib/log.js +184 -86
  114. package/lib/mail-bounce.js +107 -62
  115. package/lib/mail.js +295 -58
  116. package/lib/mcp.js +108 -27
  117. package/lib/metrics.js +98 -89
  118. package/lib/middleware/age-gate.js +36 -0
  119. package/lib/middleware/ai-act-disclosure.js +37 -0
  120. package/lib/middleware/api-encrypt.js +45 -0
  121. package/lib/middleware/assetlinks.js +40 -0
  122. package/lib/middleware/asyncapi-serve.js +35 -0
  123. package/lib/middleware/attach-user.js +40 -0
  124. package/lib/middleware/bearer-auth.js +40 -0
  125. package/lib/middleware/body-parser.js +230 -0
  126. package/lib/middleware/bot-disclose.js +34 -0
  127. package/lib/middleware/bot-guard.js +39 -0
  128. package/lib/middleware/compression.js +37 -0
  129. package/lib/middleware/cookies.js +32 -0
  130. package/lib/middleware/cors.js +40 -0
  131. package/lib/middleware/csp-nonce.js +40 -0
  132. package/lib/middleware/csp-report.js +34 -0
  133. package/lib/middleware/csrf-protect.js +43 -0
  134. package/lib/middleware/daily-byte-quota.js +53 -85
  135. package/lib/middleware/db-role-for.js +40 -0
  136. package/lib/middleware/dpop.js +40 -0
  137. package/lib/middleware/error-handler.js +37 -14
  138. package/lib/middleware/fetch-metadata.js +39 -0
  139. package/lib/middleware/flag-context.js +34 -0
  140. package/lib/middleware/gpc.js +33 -0
  141. package/lib/middleware/headers.js +35 -0
  142. package/lib/middleware/health.js +46 -0
  143. package/lib/middleware/host-allowlist.js +30 -0
  144. package/lib/middleware/network-allowlist.js +38 -0
  145. package/lib/middleware/openapi-serve.js +34 -0
  146. package/lib/middleware/rate-limit.js +160 -18
  147. package/lib/middleware/request-id.js +36 -18
  148. package/lib/middleware/request-log.js +37 -0
  149. package/lib/middleware/require-aal.js +29 -0
  150. package/lib/middleware/require-auth.js +32 -0
  151. package/lib/middleware/require-bound-key.js +41 -0
  152. package/lib/middleware/require-content-type.js +32 -0
  153. package/lib/middleware/require-methods.js +27 -0
  154. package/lib/middleware/require-mtls.js +33 -0
  155. package/lib/middleware/require-step-up.js +37 -0
  156. package/lib/middleware/security-headers.js +44 -0
  157. package/lib/middleware/security-txt.js +38 -0
  158. package/lib/middleware/span-http-server.js +37 -0
  159. package/lib/middleware/sse.js +36 -0
  160. package/lib/middleware/trace-log-correlation.js +33 -0
  161. package/lib/middleware/trace-propagate.js +32 -0
  162. package/lib/middleware/tus-upload.js +90 -0
  163. package/lib/middleware/web-app-manifest.js +53 -0
  164. package/lib/mtls-ca.js +100 -70
  165. package/lib/network-byte-quota.js +308 -0
  166. package/lib/network-heartbeat.js +135 -0
  167. package/lib/network-tls.js +534 -4
  168. package/lib/network.js +103 -0
  169. package/lib/notify.js +114 -43
  170. package/lib/ntp-check.js +192 -51
  171. package/lib/observability.js +145 -47
  172. package/lib/openapi.js +90 -44
  173. package/lib/outbox.js +99 -1
  174. package/lib/pagination.js +168 -86
  175. package/lib/parsers/index.js +16 -5
  176. package/lib/permissions.js +93 -40
  177. package/lib/pqc-agent.js +84 -8
  178. package/lib/pqc-software.js +94 -60
  179. package/lib/process-spawn.js +95 -21
  180. package/lib/pubsub.js +96 -66
  181. package/lib/queue.js +375 -54
  182. package/lib/redact.js +793 -21
  183. package/lib/render.js +139 -47
  184. package/lib/request-helpers.js +485 -121
  185. package/lib/restore-bundle.js +142 -39
  186. package/lib/restore-rollback.js +136 -45
  187. package/lib/retention.js +178 -50
  188. package/lib/retry.js +116 -33
  189. package/lib/router.js +475 -23
  190. package/lib/safe-async.js +543 -94
  191. package/lib/safe-buffer.js +337 -41
  192. package/lib/safe-json.js +467 -62
  193. package/lib/safe-jsonpath.js +285 -0
  194. package/lib/safe-schema.js +631 -87
  195. package/lib/safe-sql.js +221 -59
  196. package/lib/safe-url.js +278 -46
  197. package/lib/sandbox-worker.js +135 -0
  198. package/lib/sandbox.js +358 -0
  199. package/lib/scheduler.js +135 -70
  200. package/lib/self-update.js +647 -0
  201. package/lib/session-device-binding.js +431 -0
  202. package/lib/session.js +259 -49
  203. package/lib/slug.js +138 -26
  204. package/lib/ssrf-guard.js +316 -56
  205. package/lib/storage.js +433 -70
  206. package/lib/subject.js +405 -23
  207. package/lib/template.js +148 -8
  208. package/lib/tenant-quota.js +545 -0
  209. package/lib/testing.js +440 -53
  210. package/lib/time.js +291 -23
  211. package/lib/tls-exporter.js +239 -0
  212. package/lib/tracing.js +90 -74
  213. package/lib/uuid.js +97 -22
  214. package/lib/vault/index.js +284 -22
  215. package/lib/vault/seal-pem-file.js +66 -0
  216. package/lib/watcher.js +368 -0
  217. package/lib/webhook.js +196 -63
  218. package/lib/websocket.js +393 -68
  219. package/lib/wiki-concepts.js +338 -0
  220. package/lib/worker-pool.js +464 -0
  221. package/package.json +3 -3
  222. package/sbom.cyclonedx.json +7 -7
@@ -1,34 +1,87 @@
1
1
  "use strict";
2
2
  /**
3
- * safe-schema — declarative input validation, Zod-shaped surface.
3
+ * @module b.safeSchema
4
+ * @featured true
5
+ * @nav Validation
6
+ * @title Safe Schema
4
7
  *
5
- * Built for: request-body validation, config validation, API payload
6
- * validation, anywhere operators have an `unknown` shape they need
7
- * to confirm before reading. Vendor-free; built on framework
8
- * primitives. No JIT, no codegen, no chained-Promise weirdness.
8
+ * @intro
9
+ * Declarative input validation with a Zod-shaped chained-method
10
+ * surface. Built for request-body validation, config validation,
11
+ * API payload validation, anywhere operators have an `unknown`
12
+ * shape they need to confirm before reading. Vendor-free; built
13
+ * on framework primitives. No JIT, no codegen, no chained-Promise
14
+ * weirdness.
9
15
  *
10
- * Public API:
11
- * var s = b.safeSchema;
16
+ * Schemas are immutable — every chained check returns a new
17
+ * schema; the original is untouched. `parse(input)` throws
18
+ * `SafeSchemaError` carrying a full per-field issues array;
19
+ * `safeParse(input)` never throws and returns `{ ok, value?,
20
+ * errors? }` (operator-friendly at HTTP boundaries).
12
21
  *
13
- * var schema = s.object({
14
- * email: s.string().email(),
15
- * age: s.number().int().min(0).max(120),
16
- * tags: s.array(s.string()).max(10),
17
- * metadata: s.object({}).passthrough().optional(),
18
- * });
22
+ * Security guarantees: prototype-pollution defense — `__proto__`
23
+ * / `constructor` / `prototype` keys are rejected at object-shape
24
+ * construction AND at parse-time on object + record inputs (mirrors
25
+ * `b.safeJson`'s POISONED_KEYS). Per-format defensive length caps
26
+ * (email 254, url 8 KiB, uuid 50, datetime 100 chars …) bound input
27
+ * BEFORE the regex engine runs, so a hostile payload can't ReDoS
28
+ * `.email()` / `.url()` / `.datetime()`. All format regexes are
29
+ * static module-level constants — no string→regex parsing on the
30
+ * validation path. `.refine()` predicates that throw turn into a
31
+ * regular validation issue rather than crashing the request.
32
+ *
33
+ * Coercion is deliberately not shipped (`.coerce` is a footgun:
34
+ * "0" → 0 vs "0" → "0" ambiguity, truthy/falsy edge cases).
35
+ * Operators do explicit `s.preprocess(fn, schema)` instead.
36
+ *
37
+ * Relationship to `b.forms.validate`: forms.validate carries
38
+ * HTML-spec concerns (checkbox coercion, select option allowlist)
39
+ * that don't belong on the general-purpose validator, so the two
40
+ * surfaces stay distinct rather than one wrapping the other.
19
41
  *
20
- * var result = schema.parse(input); // throws SafeSchemaError
21
- * var safe = schema.safeParse(input); // → { ok, value?, errors? }
42
+ * Surface (every schema has these chained methods unless noted):
22
43
  *
23
- * // Errors carry per-field paths:
24
- * // [{ path: ["age"], code: "number/too-large", message: "must be ≤ 120" }]
44
+ * Type constructors:
45
+ * string() .min, .max, .length, .regex, .email, .url, .uuid,
46
+ * .datetime (ISO-8601), .date (YYYY-MM-DD),
47
+ * .ip, .ipv4, .ipv6, .nonempty, .startsWith,
48
+ * .endsWith, .includes, .cuid, .ulid, .base64,
49
+ * .trim, .toLowerCase, .toUpperCase
50
+ * number() .int, .min, .max, .gt, .lt, .positive, .negative,
51
+ * .nonnegative, .nonpositive, .finite, .safe,
52
+ * .multipleOf
53
+ * boolean()
54
+ * literal(v)
55
+ * enum_([...]) | oneOf([...])
56
+ * null_(), undefined_(), any(), unknown()
57
+ *
58
+ * Composites:
59
+ * object({...}) .strict, .passthrough, .pick, .omit, .extend,
60
+ * .partial, .required
61
+ * array(item) .min, .max, .length, .nonempty
62
+ * tuple([...]) .rest(item)
63
+ * union([...]) first matching option wins
64
+ * discriminatedUnion(key, [...]) tagged-union dispatch
65
+ * record(value) | record(key, value)
66
+ * lazy(() => schema) deferred (recursion)
67
+ * preprocess(fn, schema) pre-validation transform
68
+ *
69
+ * Modifiers (any schema):
70
+ * .optional(), .nullable(), .default(v|fn), .catch(v|fn),
71
+ * .refine(fn, opts), .transform(fn), .pipe(next)
72
+ *
73
+ * Deliberately not shipped (with structural reason): z.bigint /
74
+ * z.date / z.map / z.set (no JSON representation), z.nativeEnum
75
+ * / z.never / z.void / z.function (TypeScript-specific), z.coerce
76
+ * (security foot-gun — use s.preprocess), z.intersection
77
+ * (use .extend() for object schemas), z.brand (compile-time tag,
78
+ * no runtime effect), per-schema errorMap (chain .refine() with
79
+ * custom message instead).
25
80
  *
26
81
  * Design choices:
27
82
  * - Schemas are immutable. Chaining returns a new schema with one
28
83
  * additional check; the original is untouched. Cheap because
29
84
  * checks are concat'd into a small array, not deep-copied.
30
- * - parse() throws SafeSchemaError carrying the full issues array;
31
- * safeParse() never throws (operator-friendly for HTTP boundaries).
32
85
  * - .optional() means "may be undefined"; .nullable() means "may be
33
86
  * null"; .default(v) means "if undefined, substitute v";
34
87
  * .catch(v) means "on ANY validation failure, substitute v".
@@ -37,76 +90,10 @@
37
90
  * - Objects are STRICT by default: unknown keys produce an issue.
38
91
  * Use .passthrough() to retain unknown keys, .strict() to flip
39
92
  * back if a parent .passthrough() set the mode.
40
- *
41
- * Surface (every schema has these chained methods unless noted):
42
- *
43
- * Type constructors:
44
- * string() .min, .max, .length, .regex, .email, .url, .uuid,
45
- * .datetime (ISO-8601), .date (YYYY-MM-DD),
46
- * .ip, .ipv4, .ipv6, .nonempty, .startsWith, .endsWith,
47
- * .includes
48
- * number() .int, .min, .max, .gt, .lt, .positive, .negative,
49
- * .nonnegative, .nonpositive, .finite, .multipleOf
50
- * boolean()
51
- * literal(v)
52
- * enum_([...]) | oneOf([...])
53
- * null_(), undefined_(), any(), unknown()
54
- *
55
- * Composites:
56
- * object({ ... }) .strict, .passthrough, .pick, .omit, .extend,
57
- * .partial, .required (inverse of partial)
58
- * array(item) .min, .max, .length, .nonempty
59
- * tuple([...]) .rest(item) for variadic tails
60
- * union([...]) first matching wins
61
- * discriminatedUnion(key, [...]) faster + clearer-errors variant
62
- * for tagged unions
63
- * record(value) | record(key, value)
64
- * lazy(() => schema) defer construction; for recursion
65
- * preprocess(fn, schema) run fn before validation
66
- *
67
- * Modifiers (any schema):
68
- * .optional() value may be undefined
69
- * .nullable() value may be null
70
- * .default(v|fn) undefined → v (implies optional). Function form
71
- * is called per-parse for fresh values.
72
- * .catch(v|fn) any failure → v (escape hatch for operator
73
- * defaults; suppresses the error info, so use
74
- * sparingly)
75
- * .refine(fn, opts) custom predicate — returns false to fail
76
- * .transform(fn) map the validated value to a new shape
77
- * .pipe(next) feed validated output through `next` schema
78
- * for a second round of validation
79
- *
80
- * Security guarantees:
81
- * - Prototype-pollution defense: __proto__ / constructor / prototype
82
- * keys are rejected at construction (object shape) and parse time
83
- * (object + record input). Mirrors safe-json.js's POISONED_KEYS.
84
- * - No code injection surface: regexes are static module-level
85
- * constants; no string→regex parsing on the validation path; no
86
- * eval/Function. Operator-supplied refine/transform fns are
87
- * plain JS functions, not strings.
88
- * - Predicate throws are caught: a refine() function throwing turns
89
- * into a regular validation issue, not an unhandled exception.
90
93
  * - Sync-only: no async refinements; operators await at the boundary.
91
94
  *
92
- * Deliberately not shipped (with structural reason):
93
- * - z.bigint / z.date / z.map / z.set — no JSON representation; HTTP
94
- * boundaries don't carry these. Use s.string().datetime() for
95
- * ISO-8601 strings.
96
- * - z.nativeEnum / z.never / z.void / z.function — TypeScript-specific.
97
- * - z.coerce — loose-coercion is a security foot-gun (truthy/falsy
98
- * ambiguity, "0" → 0 vs "0" → "0"). Operators do explicit
99
- * s.preprocess(fn, schema) instead.
100
- * - z.intersection — for object schemas use .extend(); intersections
101
- * of unrelated schemas are structurally ambiguous.
102
- * - z.brand — TypeScript compile-time tag with no runtime effect.
103
- * - per-schema errorMap — operators chain .refine() with custom message.
104
- *
105
- * Relationship to forms.validate:
106
- * forms.validate (HTML form spec validation) is a separate surface.
107
- * Form specs carry HTML-specific concerns (checkbox coercion, select
108
- * option allowlist) that don't belong on the general-purpose validator,
109
- * so the two stay distinct rather than one wrapping the other.
95
+ * @card
96
+ * Declarative input validation with a Zod-shaped chained-method surface.
110
97
  */
111
98
 
112
99
  var C = require("./constants");
@@ -129,6 +116,35 @@ var DATETIME_MAX_LEN = 100; // ISO-8601 with offset + fractional seconds tops
129
116
  var CUID_MAX_LEN = 50; // CUID v1/v2 is 25 chars
130
117
  var ULID_MAX_LEN = 50; // ULID is exactly 26 chars
131
118
 
119
+ /**
120
+ * @primitive b.safeSchema.SafeSchemaError
121
+ * @signature b.safeSchema.SafeSchemaError
122
+ * @since 0.1.0
123
+ * @status stable
124
+ * @related b.safeSchema.string, b.safeSchema.object
125
+ *
126
+ * Error class thrown by every `b.safeSchema` primitive on
127
+ * construction-time misuse (bad shape / bad enum / bad union /
128
+ * poisoned key) and by `schema.parse(...)` on validation failure.
129
+ * Built via `b.framework.defineClass` and marked `alwaysPermanent`
130
+ * so it never round-trips through retry or transient-error logic.
131
+ * The thrown instance carries `.issues` — the full per-field
132
+ * issues array (`{ path, code, message }[]`) — so HTTP middleware
133
+ * can surface every failure in one 400 response.
134
+ *
135
+ * @example
136
+ * var b = require("blamejs");
137
+ * var s = b.safeSchema;
138
+ *
139
+ * try {
140
+ * s.string().min(3).parse("ab");
141
+ * } catch (e) {
142
+ * e instanceof s.SafeSchemaError;
143
+ * // → true
144
+ * e.issues[0].code;
145
+ * // → "string/too-short"
146
+ * }
147
+ */
132
148
  var SafeSchemaError = defineClass("SafeSchemaError", { alwaysPermanent: true });
133
149
 
134
150
  // Prototype-pollution defense — these key names are rejected in object
@@ -360,6 +376,43 @@ function _withCheck(schema, spec, check) {
360
376
 
361
377
  // ---- string ----
362
378
 
379
+ /**
380
+ * @primitive b.safeSchema.string
381
+ * @signature b.safeSchema.string()
382
+ * @since 0.1.0
383
+ * @status stable
384
+ * @related b.safeSchema.number, b.safeSchema.object
385
+ *
386
+ * Construct a string-typed schema. Chain `.min`, `.max`, `.length`,
387
+ * `.regex`, `.email`, `.url`, `.uuid`, `.datetime`, `.date`, `.ipv4`,
388
+ * `.ipv6`, `.ip`, `.cuid`, `.ulid`, `.base64`, `.startsWith`,
389
+ * `.endsWith`, `.includes`, `.nonempty`, `.trim`, `.toLowerCase`,
390
+ * `.toUpperCase` to add checks and coercions. Each chained call
391
+ * returns a new immutable schema.
392
+ *
393
+ * The named-format methods (`.email` / `.url` / `.uuid` / etc.)
394
+ * apply a defensive length cap BEFORE running the regex so a
395
+ * hostile payload cannot drive the regex engine with an arbitrarily
396
+ * long string.
397
+ *
398
+ * @example
399
+ * var b = require("blamejs");
400
+ * var s = b.safeSchema;
401
+ *
402
+ * var name = s.string().min(1).max(80);
403
+ * name.parse("alice");
404
+ * // → "alice"
405
+ *
406
+ * var email = s.string().trim().toLowerCase().email();
407
+ * email.parse(" Alice@Example.COM ");
408
+ * // → "alice@example.com"
409
+ *
410
+ * var safe = s.string().min(3).safeParse("ab");
411
+ * safe.ok;
412
+ * // → false
413
+ * safe.errors[0].code;
414
+ * // → "string/too-short"
415
+ */
363
416
  function string() {
364
417
  var spec = {
365
418
  kind: "string",
@@ -554,6 +607,37 @@ function _stringMethods(schema, spec) {
554
607
 
555
608
  // ---- number ----
556
609
 
610
+ /**
611
+ * @primitive b.safeSchema.number
612
+ * @signature b.safeSchema.number()
613
+ * @since 0.1.0
614
+ * @status stable
615
+ * @related b.safeSchema.string, b.safeSchema.literal
616
+ *
617
+ * Construct a number-typed schema. Rejects `NaN` at the type check.
618
+ * Chain `.int`, `.min`, `.max`, `.gt`, `.lt`, `.positive`,
619
+ * `.negative`, `.nonnegative`, `.nonpositive`, `.finite`, `.safe`,
620
+ * `.multipleOf` to bound the value. `.safe()` enforces the
621
+ * `Number.isSafeInteger` range — important for IDs that round-trip
622
+ * through JSON (no BigInt support) and need to survive without
623
+ * precision loss.
624
+ *
625
+ * @example
626
+ * var b = require("blamejs");
627
+ * var s = b.safeSchema;
628
+ *
629
+ * var age = s.number().int().min(0).max(150);
630
+ * age.parse(30);
631
+ * // → 30
632
+ *
633
+ * try { age.parse(200); }
634
+ * catch (e) { e.issues[0].code; }
635
+ * // → "number/too-large"
636
+ *
637
+ * var price = s.number().finite().multipleOf(0.01);
638
+ * price.parse(19.99);
639
+ * // → 19.99
640
+ */
557
641
  function number() {
558
642
  var spec = {
559
643
  kind: "number",
@@ -630,6 +714,29 @@ function _numberMethods(schema, spec) {
630
714
 
631
715
  // ---- boolean ----
632
716
 
717
+ /**
718
+ * @primitive b.safeSchema.boolean
719
+ * @signature b.safeSchema.boolean()
720
+ * @since 0.1.0
721
+ * @status stable
722
+ * @related b.safeSchema.literal, b.safeSchema.preprocess
723
+ *
724
+ * Construct a boolean-typed schema. Strict `typeof === "boolean"`
725
+ * check — does not coerce truthy/falsy values. To accept the strings
726
+ * `"true"` / `"false"` from query parameters, wrap in
727
+ * `s.preprocess(fn, s.boolean())`.
728
+ *
729
+ * @example
730
+ * var b = require("blamejs");
731
+ * var s = b.safeSchema;
732
+ *
733
+ * s.boolean().parse(true);
734
+ * // → true
735
+ *
736
+ * try { s.boolean().parse("true"); }
737
+ * catch (e) { e.issues[0].code; }
738
+ * // → "type"
739
+ */
633
740
  function boolean() {
634
741
  return _baseSchema({
635
742
  kind: "boolean",
@@ -642,6 +749,29 @@ function boolean() {
642
749
 
643
750
  // ---- literal ----
644
751
 
752
+ /**
753
+ * @primitive b.safeSchema.literal
754
+ * @signature b.safeSchema.literal(expected)
755
+ * @since 0.1.0
756
+ * @status stable
757
+ * @related b.safeSchema.enum_, b.safeSchema.discriminatedUnion
758
+ *
759
+ * Construct a schema that accepts exactly one specific value
760
+ * (compared via `===`). Useful as the discriminator on tagged
761
+ * unions — see `s.discriminatedUnion`.
762
+ *
763
+ * @example
764
+ * var b = require("blamejs");
765
+ * var s = b.safeSchema;
766
+ *
767
+ * var version = s.literal("v1");
768
+ * version.parse("v1");
769
+ * // → "v1"
770
+ *
771
+ * try { version.parse("v2"); }
772
+ * catch (e) { e.issues[0].code; }
773
+ * // → "literal"
774
+ */
645
775
  function literal(expected) {
646
776
  return _baseSchema({
647
777
  kind: "literal",
@@ -656,6 +786,36 @@ function literal(expected) {
656
786
 
657
787
  // ---- enum / oneOf ----
658
788
 
789
+ /**
790
+ * @primitive b.safeSchema.enum_
791
+ * @signature b.safeSchema.enum_(values)
792
+ * @since 0.1.0
793
+ * @status stable
794
+ * @related b.safeSchema.literal, b.safeSchema.union
795
+ *
796
+ * Construct a schema that accepts any value from the given
797
+ * non-empty array (compared via `Set.has`). Also exported as
798
+ * `b.safeSchema.oneOf` because `enum` is a reserved word in some
799
+ * tooling. Throws `SafeSchemaError` (`safe-schema/bad-enum`) when
800
+ * `values` is not a non-empty array.
801
+ *
802
+ * @example
803
+ * var b = require("blamejs");
804
+ * var s = b.safeSchema;
805
+ *
806
+ * var role = s.enum_(["admin", "editor", "viewer"]);
807
+ * role.parse("editor");
808
+ * // → "editor"
809
+ *
810
+ * try { role.parse("guest"); }
811
+ * catch (e) { e.issues[0].code; }
812
+ * // → "enum"
813
+ *
814
+ * // oneOf is the same primitive under a friendlier name.
815
+ * var same = s.oneOf(["a", "b"]);
816
+ * same.parse("a");
817
+ * // → "a"
818
+ */
659
819
  function enum_(values) {
660
820
  if (!Array.isArray(values) || values.length === 0) {
661
821
  throw new SafeSchemaError("safe-schema/bad-enum",
@@ -676,6 +836,29 @@ function enum_(values) {
676
836
 
677
837
  // ---- null / undefined / any / unknown ----
678
838
 
839
+ /**
840
+ * @primitive b.safeSchema.null_
841
+ * @signature b.safeSchema.null_()
842
+ * @since 0.1.0
843
+ * @status stable
844
+ * @related b.safeSchema.undefined_, b.safeSchema.any
845
+ *
846
+ * Construct a schema that accepts only `null`. Trailing-underscore
847
+ * name because `null` is a reserved word. Useful inside unions
848
+ * (e.g. `s.union([s.string(), s.null_()])`), though
849
+ * `s.string().nullable()` is the more common idiom.
850
+ *
851
+ * @example
852
+ * var b = require("blamejs");
853
+ * var s = b.safeSchema;
854
+ *
855
+ * s.null_().parse(null);
856
+ * // → null
857
+ *
858
+ * try { s.null_().parse(0); }
859
+ * catch (e) { e.issues[0].code; }
860
+ * // → "type"
861
+ */
679
862
  function null_() {
680
863
  return _baseSchema({
681
864
  kind: "null",
@@ -687,6 +870,28 @@ function null_() {
687
870
  });
688
871
  }
689
872
 
873
+ /**
874
+ * @primitive b.safeSchema.undefined_
875
+ * @signature b.safeSchema.undefined_()
876
+ * @since 0.1.0
877
+ * @status stable
878
+ * @related b.safeSchema.null_, b.safeSchema.any
879
+ *
880
+ * Construct a schema that accepts only `undefined`. Trailing-
881
+ * underscore name because `undefined` shadows poorly. The schema is
882
+ * implicitly `optional` — `parse(undefined)` succeeds.
883
+ *
884
+ * @example
885
+ * var b = require("blamejs");
886
+ * var s = b.safeSchema;
887
+ *
888
+ * s.undefined_().parse(undefined);
889
+ * // → undefined
890
+ *
891
+ * try { s.undefined_().parse(null); }
892
+ * catch (e) { e.issues[0].code; }
893
+ * // → "type"
894
+ */
690
895
  function undefined_() {
691
896
  return _baseSchema({
692
897
  kind: "undefined",
@@ -697,6 +902,28 @@ function undefined_() {
697
902
  });
698
903
  }
699
904
 
905
+ /**
906
+ * @primitive b.safeSchema.any
907
+ * @signature b.safeSchema.any()
908
+ * @since 0.1.0
909
+ * @status stable
910
+ * @related b.safeSchema.unknown, b.safeSchema.preprocess
911
+ *
912
+ * Construct a schema that accepts any value, including `null` and
913
+ * `undefined`. Useful as a placeholder while iterating on a schema
914
+ * shape, or inside `s.record(s.any())` when the operator wants the
915
+ * keys validated but not the values.
916
+ *
917
+ * @example
918
+ * var b = require("blamejs");
919
+ * var s = b.safeSchema;
920
+ *
921
+ * s.any().parse({ anything: "goes" });
922
+ * // → { anything: "goes" }
923
+ *
924
+ * s.any().parse(null);
925
+ * // → null
926
+ */
700
927
  function any() {
701
928
  return _baseSchema({
702
929
  kind: "any",
@@ -706,10 +933,73 @@ function any() {
706
933
  });
707
934
  }
708
935
 
936
+ /**
937
+ * @primitive b.safeSchema.unknown
938
+ * @signature b.safeSchema.unknown()
939
+ * @since 0.1.0
940
+ * @status stable
941
+ * @related b.safeSchema.any
942
+ *
943
+ * Alias for `b.safeSchema.any`. Some operators prefer the spelling
944
+ * `unknown` to signal "we accept anything but expect downstream
945
+ * code to narrow the type". Behavior is identical.
946
+ *
947
+ * @example
948
+ * var b = require("blamejs");
949
+ * var s = b.safeSchema;
950
+ *
951
+ * s.unknown().parse({ raw: 1 });
952
+ * // → { raw: 1 }
953
+ */
709
954
  function unknown() { return any(); }
710
955
 
711
956
  // ---- object ----
712
957
 
958
+ /**
959
+ * @primitive b.safeSchema.object
960
+ * @signature b.safeSchema.object(shape)
961
+ * @since 0.1.0
962
+ * @status stable
963
+ * @related b.safeSchema.record, b.safeSchema.discriminatedUnion
964
+ *
965
+ * Construct an object schema from a `{ key: schema }` shape map.
966
+ * Strict by default — unknown keys produce an `object/unknown-key`
967
+ * issue. Chain `.passthrough()` to retain extras, `.strict()` to
968
+ * flip back, `.pick`, `.omit`, `.extend`, `.partial`, `.required`
969
+ * to derive related shapes.
970
+ *
971
+ * Prototype-pollution defense — `__proto__` / `constructor` /
972
+ * `prototype` keys are rejected at shape-construction time AND at
973
+ * parse time regardless of mode (`.passthrough()` does NOT permit
974
+ * them). Throws `SafeSchemaError`
975
+ * (`safe-schema/poisoned-shape-key` / `safe-schema/bad-shape`) on
976
+ * invalid input.
977
+ *
978
+ * @example
979
+ * var b = require("blamejs");
980
+ * var s = b.safeSchema;
981
+ *
982
+ * var user = s.object({
983
+ * email: s.string().email(),
984
+ * age: s.number().int().min(0).max(150),
985
+ * });
986
+ *
987
+ * user.parse({ email: "alice@example.com", age: 30 });
988
+ * // → { email: "alice@example.com", age: 30 }
989
+ *
990
+ * // Unknown keys rejected by default.
991
+ * try { user.parse({ email: "a@b.com", age: 30, extra: 1 }); }
992
+ * catch (e) { e.issues[0].code; }
993
+ * // → "object/unknown-key"
994
+ *
995
+ * // Prototype-pollution attempt rejected even with passthrough.
996
+ * var loose = user.passthrough();
997
+ * var report = loose.safeParse({ email: "a@b.com", age: 30, __proto__: { admin: true } });
998
+ * report.ok;
999
+ * // → false
1000
+ * report.errors[0].code;
1001
+ * // → "object/poisoned-key"
1002
+ */
713
1003
  function object(shape) {
714
1004
  if (shape == null || typeof shape !== "object") {
715
1005
  throw new SafeSchemaError("safe-schema/bad-shape",
@@ -861,6 +1151,33 @@ function _objectWithMode(shape, keys, mode) {
861
1151
 
862
1152
  // ---- array ----
863
1153
 
1154
+ /**
1155
+ * @primitive b.safeSchema.array
1156
+ * @signature b.safeSchema.array(itemSchema)
1157
+ * @since 0.1.0
1158
+ * @status stable
1159
+ * @related b.safeSchema.tuple, b.safeSchema.record
1160
+ *
1161
+ * Construct an array schema where every element is validated
1162
+ * against `itemSchema`. Chain `.min`, `.max`, `.length`, `.nonempty`
1163
+ * to bound the length. Issues from individual items carry their
1164
+ * index in the path (e.g. `[3]`). Throws `SafeSchemaError`
1165
+ * (`safe-schema/bad-item`) when the item argument is not a schema.
1166
+ *
1167
+ * @example
1168
+ * var b = require("blamejs");
1169
+ * var s = b.safeSchema;
1170
+ *
1171
+ * var tags = s.array(s.string().min(1)).max(10);
1172
+ * tags.parse(["alpha", "beta"]);
1173
+ * // → ["alpha", "beta"]
1174
+ *
1175
+ * var report = tags.safeParse(["ok", "", "also-ok"]);
1176
+ * report.ok;
1177
+ * // → false
1178
+ * report.errors[0].path;
1179
+ * // → [1]
1180
+ */
864
1181
  function array(itemSchema) {
865
1182
  if (!itemSchema || typeof itemSchema._run !== "function") {
866
1183
  throw new SafeSchemaError("safe-schema/bad-item",
@@ -917,6 +1234,32 @@ function _arrayMethods(schema, spec) {
917
1234
 
918
1235
  // ---- tuple ----
919
1236
 
1237
+ /**
1238
+ * @primitive b.safeSchema.tuple
1239
+ * @signature b.safeSchema.tuple(items)
1240
+ * @since 0.1.0
1241
+ * @status stable
1242
+ * @related b.safeSchema.array, b.safeSchema.union
1243
+ *
1244
+ * Construct a fixed-length heterogeneous array schema. `items` is
1245
+ * a non-empty array of schemas, one per slot. Chain `.rest(item)`
1246
+ * to allow a variadic tail (common for `[verb, ...args]` /
1247
+ * `[event, payload, ...metadata]` shapes). Throws
1248
+ * `SafeSchemaError` (`safe-schema/bad-tuple`) on invalid input.
1249
+ *
1250
+ * @example
1251
+ * var b = require("blamejs");
1252
+ * var s = b.safeSchema;
1253
+ *
1254
+ * var pair = s.tuple([s.string(), s.number()]);
1255
+ * pair.parse(["count", 42]);
1256
+ * // → ["count", 42]
1257
+ *
1258
+ * // Variadic tail via .rest()
1259
+ * var event = s.tuple([s.string()]).rest(s.number());
1260
+ * event.parse(["sum", 1, 2, 3]);
1261
+ * // → ["sum", 1, 2, 3]
1262
+ */
920
1263
  function tuple(items) {
921
1264
  if (!Array.isArray(items) || items.length === 0) {
922
1265
  throw new SafeSchemaError("safe-schema/bad-tuple",
@@ -983,6 +1326,35 @@ function _tupleWithRest(items, restSchema) {
983
1326
 
984
1327
  // ---- union ----
985
1328
 
1329
+ /**
1330
+ * @primitive b.safeSchema.union
1331
+ * @signature b.safeSchema.union(options)
1332
+ * @since 0.1.0
1333
+ * @status stable
1334
+ * @related b.safeSchema.discriminatedUnion, b.safeSchema.enum_
1335
+ *
1336
+ * Construct a schema that accepts a value matching ANY of the
1337
+ * given option schemas. First match wins. When no option matches,
1338
+ * issues from every branch are collected in the failure for deep
1339
+ * diagnostics, plus a parent-level `union` issue summarizing the
1340
+ * miss. For tagged-variant shapes prefer
1341
+ * `s.discriminatedUnion` — it dispatches in O(1) on the tag and
1342
+ * produces clearer error messages.
1343
+ *
1344
+ * @example
1345
+ * var b = require("blamejs");
1346
+ * var s = b.safeSchema;
1347
+ *
1348
+ * var idOrName = s.union([s.number().int().positive(), s.string().min(1)]);
1349
+ * idOrName.parse(42);
1350
+ * // → 42
1351
+ * idOrName.parse("alice");
1352
+ * // → "alice"
1353
+ *
1354
+ * try { idOrName.parse(true); }
1355
+ * catch (e) { e.issues[0].code; }
1356
+ * // → "union"
1357
+ */
986
1358
  function union(options) {
987
1359
  if (!Array.isArray(options) || options.length === 0) {
988
1360
  throw new SafeSchemaError("safe-schema/bad-union",
@@ -1020,6 +1392,34 @@ function union(options) {
1020
1392
  // record(value) — string keys, schema-typed values
1021
1393
  // record(keySchema, value) — both keys and values are schema-validated
1022
1394
 
1395
+ /**
1396
+ * @primitive b.safeSchema.record
1397
+ * @signature b.safeSchema.record(a, b)
1398
+ * @since 0.1.0
1399
+ * @status stable
1400
+ * @related b.safeSchema.object, b.safeSchema.array
1401
+ *
1402
+ * Construct a schema for an object whose KEYS are arbitrary strings
1403
+ * and whose VALUES match a given schema. Two call shapes:
1404
+ * `record(valueSchema)` accepts any string key;
1405
+ * `record(keySchema, valueSchema)` validates both keys and values.
1406
+ * Prototype-pollution defense — `__proto__` / `constructor` /
1407
+ * `prototype` keys are rejected at parse time, mirroring `s.object`.
1408
+ * Throws `SafeSchemaError` on invalid arguments.
1409
+ *
1410
+ * @example
1411
+ * var bjs = require("blamejs");
1412
+ * var s = bjs.safeSchema;
1413
+ *
1414
+ * var counts = s.record(s.number().int().nonnegative());
1415
+ * counts.parse({ apples: 3, pears: 0 });
1416
+ * // → { apples: 3, pears: 0 }
1417
+ *
1418
+ * // Validate keys too: only ULIDs allowed.
1419
+ * var byId = s.record(s.string().ulid(), s.string());
1420
+ * byId.parse({ "01HF5Z6Q9P8R7S4T3V2W1X0Y9Z": "alice" });
1421
+ * // → { "01HF5Z6Q9P8R7S4T3V2W1X0Y9Z": "alice" }
1422
+ */
1023
1423
  function record(a, b) {
1024
1424
  var keySchema, valueSchema;
1025
1425
  if (b === undefined) {
@@ -1093,6 +1493,41 @@ function record(a, b) {
1093
1493
  // literal schema. Mismatched discriminator fails fast with a clear
1094
1494
  // "expected one of [...]" message rather than burying the operator in
1095
1495
  // per-branch issues.
1496
+ /**
1497
+ * @primitive b.safeSchema.discriminatedUnion
1498
+ * @signature b.safeSchema.discriminatedUnion(discriminator, options)
1499
+ * @since 0.1.0
1500
+ * @status stable
1501
+ * @related b.safeSchema.union, b.safeSchema.literal
1502
+ *
1503
+ * Construct a tagged-union schema. `discriminator` names a key
1504
+ * present on every option as a `s.literal(...)` schema; the
1505
+ * validator dispatches in O(1) on that key's value rather than
1506
+ * trying every option in turn (faster + clearer error messages
1507
+ * than `s.union`). Every option must be an object schema whose
1508
+ * shape carries a literal at the discriminator key. The
1509
+ * discriminator name itself cannot be `__proto__` /
1510
+ * `constructor` / `prototype`. Throws `SafeSchemaError` on
1511
+ * invalid input.
1512
+ *
1513
+ * @example
1514
+ * var b = require("blamejs");
1515
+ * var s = b.safeSchema;
1516
+ *
1517
+ * var event = s.discriminatedUnion("kind", [
1518
+ * s.object({ kind: s.literal("created"), at: s.string().datetime() }),
1519
+ * s.object({ kind: s.literal("deleted"), reason: s.string() }),
1520
+ * ]);
1521
+ *
1522
+ * event.parse({ kind: "created", at: "2026-01-01T00:00:00Z" });
1523
+ * // → { kind: "created", at: "2026-01-01T00:00:00Z" }
1524
+ *
1525
+ * var report = event.safeParse({ kind: "unknown" });
1526
+ * report.ok;
1527
+ * // → false
1528
+ * report.errors[0].code;
1529
+ * // → "discriminated-union/no-match"
1530
+ */
1096
1531
  function discriminatedUnion(discriminator, options) {
1097
1532
  if (typeof discriminator !== "string" || discriminator.length === 0) {
1098
1533
  throw new SafeSchemaError("safe-schema/bad-discriminator",
@@ -1155,6 +1590,42 @@ function discriminatedUnion(discriminator, options) {
1155
1590
  //
1156
1591
  // fn errors propagate as a 'preprocess' issue at the parent path; they
1157
1592
  // don't crash the validate call.
1593
+ /**
1594
+ * @primitive b.safeSchema.preprocess
1595
+ * @signature b.safeSchema.preprocess(fn, inner)
1596
+ * @since 0.1.0
1597
+ * @status stable
1598
+ * @related b.safeSchema.string, b.safeSchema.number
1599
+ *
1600
+ * Wrap a schema with a transform that runs BEFORE validation.
1601
+ * Common at HTTP boundaries where query strings arrive as strings
1602
+ * but the operator wants a number / boolean schema downstream.
1603
+ * Errors thrown by `fn` propagate as a `preprocess` issue at the
1604
+ * parent path rather than crashing the parse. Throws
1605
+ * `SafeSchemaError` (`safe-schema/bad-preprocess`) when args are
1606
+ * the wrong shape.
1607
+ *
1608
+ * Prefer `s.preprocess` over a hypothetical `.coerce` because
1609
+ * coercion ambiguity (`"0" → 0` vs `"0" → "0"`) is a security
1610
+ * foot-gun; `s.preprocess` makes the conversion explicit at the
1611
+ * call site.
1612
+ *
1613
+ * @example
1614
+ * var b = require("blamejs");
1615
+ * var s = b.safeSchema;
1616
+ *
1617
+ * var port = s.preprocess(
1618
+ * function (v) { return Number(v); },
1619
+ * s.number().int().min(1).max(65535)
1620
+ * );
1621
+ *
1622
+ * port.parse("8080");
1623
+ * // → 8080
1624
+ *
1625
+ * var report = port.safeParse("not-a-port");
1626
+ * report.ok;
1627
+ * // → false
1628
+ */
1158
1629
  function preprocess(fn, inner) {
1159
1630
  if (typeof fn !== "function") {
1160
1631
  throw new SafeSchemaError("safe-schema/bad-preprocess",
@@ -1192,6 +1663,34 @@ function preprocess(fn, inner) {
1192
1663
  //
1193
1664
  // The function is called lazily and cached per-call site; cycles in the
1194
1665
  // returned schema are fine.
1666
+ /**
1667
+ * @primitive b.safeSchema.lazy
1668
+ * @signature b.safeSchema.lazy(getter)
1669
+ * @since 0.1.0
1670
+ * @status stable
1671
+ * @related b.safeSchema.object, b.safeSchema.array
1672
+ *
1673
+ * Defer schema construction until first parse, enabling recursive
1674
+ * shapes (comment threads, file-tree nodes, AST nodes). `getter` is
1675
+ * a no-arg function that returns the schema; it's called once on
1676
+ * the first parse and cached. The returned schema can reference
1677
+ * its enclosing variable, breaking the chicken-and-egg cycle.
1678
+ * Throws `SafeSchemaError` (`safe-schema/bad-lazy`) when `getter`
1679
+ * is not a function.
1680
+ *
1681
+ * @example
1682
+ * var b = require("blamejs");
1683
+ * var s = b.safeSchema;
1684
+ *
1685
+ * var commentSchema = s.object({
1686
+ * id: s.string(),
1687
+ * replies: s.array(s.lazy(function () { return commentSchema; })),
1688
+ * });
1689
+ *
1690
+ * var input = { id: "a", replies: [{ id: "b", replies: [] }] };
1691
+ * commentSchema.parse(input);
1692
+ * // → { id: "a", replies: [{ id: "b", replies: [] }] }
1693
+ */
1195
1694
  function lazy(getter) {
1196
1695
  if (typeof getter !== "function") {
1197
1696
  throw new SafeSchemaError("safe-schema/bad-lazy",
@@ -1215,7 +1714,52 @@ function lazy(getter) {
1215
1714
 
1216
1715
  // ---- top-level modifier helpers ----
1217
1716
 
1717
+ /**
1718
+ * @primitive b.safeSchema.optional
1719
+ * @signature b.safeSchema.optional(inner)
1720
+ * @since 0.1.0
1721
+ * @status stable
1722
+ * @related b.safeSchema.nullable
1723
+ *
1724
+ * Composition-style alias for `inner.optional()`. Returns a schema
1725
+ * that accepts `undefined` in addition to whatever `inner` accepts.
1726
+ * Equivalent to chaining `.optional()` on the schema; provided for
1727
+ * operators who prefer composition over chaining (e.g. mapping over
1728
+ * a list of schemas).
1729
+ *
1730
+ * @example
1731
+ * var b = require("blamejs");
1732
+ * var s = b.safeSchema;
1733
+ *
1734
+ * var maybeName = s.optional(s.string().min(1));
1735
+ * maybeName.parse(undefined);
1736
+ * // → undefined
1737
+ * maybeName.parse("alice");
1738
+ * // → "alice"
1739
+ */
1218
1740
  function optional(inner) { return inner.optional(); }
1741
+
1742
+ /**
1743
+ * @primitive b.safeSchema.nullable
1744
+ * @signature b.safeSchema.nullable(inner)
1745
+ * @since 0.1.0
1746
+ * @status stable
1747
+ * @related b.safeSchema.optional
1748
+ *
1749
+ * Composition-style alias for `inner.nullable()`. Returns a schema
1750
+ * that accepts `null` in addition to whatever `inner` accepts.
1751
+ * Compose with `s.optional` for "may be undefined OR null".
1752
+ *
1753
+ * @example
1754
+ * var b = require("blamejs");
1755
+ * var s = b.safeSchema;
1756
+ *
1757
+ * var maybeAge = s.nullable(s.number().int().min(0));
1758
+ * maybeAge.parse(null);
1759
+ * // → null
1760
+ * maybeAge.parse(30);
1761
+ * // → 30
1762
+ */
1219
1763
  function nullable(inner) { return inner.nullable(); }
1220
1764
 
1221
1765
  module.exports = {