@ultimat3/schema 9.0.0 → 10.0.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/CLAUDE.md +8 -0
- package/package.json +1 -1
- package/src/errors.ts +24 -2
- package/src/json-schema.ts +10 -1
package/CLAUDE.md
CHANGED
|
@@ -43,6 +43,14 @@ carries a duplicate of these titles and registers them unconditionally, so every
|
|
|
43
43
|
real titles just by importing core. Add a code here **and** update that duplicate in the same
|
|
44
44
|
change — `schema-error-codes-pin.test.ts` in `@ultimat3/cli` fails the build if they disagree.
|
|
45
45
|
|
|
46
|
+
`ERROR_DOCS_URL` in `errors.ts` is the third deliberate tier-0 duplicate, beside `singleLine` and
|
|
47
|
+
`ULTIMATE_ERROR_BRAND` — one URL, spelled out, because `SchemaError` cannot import
|
|
48
|
+
`@ultimat3/core`'s constant. There is no per-code URL anywhere in the framework: codes live in
|
|
49
|
+
`wiki/Error-Codes.md` as table ROWS and a table row has no anchor, so
|
|
50
|
+
`https://ultimate.dev/errors/<code>` was a 404 on every error and was deleted `As of 2026-08-23`. Change it
|
|
51
|
+
here and in `packages/core/src/error-codes.ts` in the same edit. **There is no pin test yet** —
|
|
52
|
+
one belongs in `@ultimat3/cli` beside `single-line-pin.test.ts`, which may legally import both.
|
|
53
|
+
|
|
46
54
|
`MoneyValue` in `money-value.ts` — its own file, because it is the only builtin whose *shape* other
|
|
47
55
|
packages alias — is the framework's **one** declaration of a money value. Tier 0 is
|
|
48
56
|
the only tier every package may import, and `@ultimat3/money`'s `Money` and `@ultimat3/entity`'s
|
package/package.json
CHANGED
package/src/errors.ts
CHANGED
|
@@ -23,6 +23,10 @@ const ESCAPES: Readonly<Record<string, string>> = {
|
|
|
23
23
|
'\b': String.raw`\b`,
|
|
24
24
|
'\f': String.raw`\f`,
|
|
25
25
|
};
|
|
26
|
+
// `ESCAPES[char]` is a computed read on a plain object and is safe by DOMAIN, not by luck: the
|
|
27
|
+
// key is whatever `CONTROL` matched, so it is exactly one control character — and no
|
|
28
|
+
// `Object.prototype` member has a single-character name. Every other computed read in this file
|
|
29
|
+
// goes through `Object.hasOwn`.
|
|
26
30
|
const singleLine = (text: string): string =>
|
|
27
31
|
text.replace(
|
|
28
32
|
CONTROL,
|
|
@@ -32,6 +36,18 @@ const singleLine = (text: string): string =>
|
|
|
32
36
|
/** Same well-known symbol `@ultimat3/core` brands with. Keep in sync, never rename. */
|
|
33
37
|
export const ULTIMATE_ERROR_BRAND: unique symbol = Symbol.for('ultimate.error');
|
|
34
38
|
|
|
39
|
+
/**
|
|
40
|
+
* The same one URL `@ultimat3/core`'s `ERROR_DOCS_URL` holds. **Keep in sync**, and read the
|
|
41
|
+
* reason there — briefly: `wiki/` is the only public documentation surface, codes live on that
|
|
42
|
+
* page in table ROWS, and a table row has no anchor, so there is no per-code URL to build.
|
|
43
|
+
*
|
|
44
|
+
* Spelled out rather than imported for the same reason `singleLine` and the brand symbol above
|
|
45
|
+
* are: `schema` and `core` are both tier 0, so `schema` may not import `core` (imports go DOWN,
|
|
46
|
+
* never sideways). Neither tier-0 package can check the copy against its source, so the pin
|
|
47
|
+
* belongs in `@ultimat3/cli` beside `single-line-pin.test.ts` — it is NOT written yet.
|
|
48
|
+
*/
|
|
49
|
+
const ERROR_DOCS_URL = 'https://github.com/developerz-ai/ultimate/wiki/Error-Codes';
|
|
50
|
+
|
|
35
51
|
export interface SchemaErrorCodeDeclaration {
|
|
36
52
|
readonly title: string;
|
|
37
53
|
readonly docs?: string | undefined;
|
|
@@ -100,7 +116,13 @@ export class SchemaError extends Error {
|
|
|
100
116
|
// A schema cause is the one most likely to carry a hostile string: it describes the value that
|
|
101
117
|
// failed validation, which is the request body.
|
|
102
118
|
const code = singleLine(init.code);
|
|
103
|
-
|
|
119
|
+
// `Object.hasOwn`, never the read alone: `init.code` is a bare string an app or a provider
|
|
120
|
+
// chose, so `TITLES['constructor']` answered with the `Object` FUNCTION and `singleLine` then
|
|
121
|
+
// called `.replace` on it — the error that reports a bad value died reporting it, and the
|
|
122
|
+
// caller got a `TypeError` in place of its own failure. Same discriminator as
|
|
123
|
+
// `@ultimat3/action`'s `IRREGULAR[word]`.
|
|
124
|
+
const declared = Object.hasOwn(TITLES, init.code) ? TITLES[init.code] : undefined;
|
|
125
|
+
const title = singleLine(declared ?? humanize(init.code));
|
|
104
126
|
const cause = singleLine(init.cause);
|
|
105
127
|
// The cause is in `message` for the reason `UltimateError`'s constructor gives: `message` is
|
|
106
128
|
// the ONLY field a runtime prints when an error escapes uncaught — a worker log, a CI
|
|
@@ -112,7 +134,7 @@ export class SchemaError extends Error {
|
|
|
112
134
|
this.code = code;
|
|
113
135
|
this.title = title;
|
|
114
136
|
this.fix = singleLine(init.fix);
|
|
115
|
-
this.docs = singleLine(init.docs ??
|
|
137
|
+
this.docs = singleLine(init.docs ?? ERROR_DOCS_URL);
|
|
116
138
|
this.meta = init.meta;
|
|
117
139
|
}
|
|
118
140
|
|
package/src/json-schema.ts
CHANGED
|
@@ -75,10 +75,19 @@ const FORMAT_MAP: Readonly<Record<string, string>> = Object.freeze({
|
|
|
75
75
|
cursor: 'ultimate-cursor',
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* `Object.hasOwn`, never the read alone: `node.format` comes from a schema PROVIDER's IR, so
|
|
80
|
+
* `FORMAT_MAP['constructor']` answered with the `Object` function and `?? node.format` could not
|
|
81
|
+
* rescue it — `JSON.stringify` then dropped `format` from the published document in silence.
|
|
82
|
+
* Same discriminator as `@ultimat3/action`'s `BY_FORMAT[node.format]`, one package up.
|
|
83
|
+
*/
|
|
84
|
+
const publishedFormat = (format: string): string =>
|
|
85
|
+
Object.hasOwn(FORMAT_MAP, format) ? (FORMAT_MAP[format] ?? format) : format;
|
|
86
|
+
|
|
78
87
|
function stringNode(node: SchemaNode): JsonSchema {
|
|
79
88
|
return {
|
|
80
89
|
type: 'string',
|
|
81
|
-
...(node.format === undefined ? {} : { format:
|
|
90
|
+
...(node.format === undefined ? {} : { format: publishedFormat(node.format) }),
|
|
82
91
|
...(node.minLength === undefined ? {} : { minLength: node.minLength }),
|
|
83
92
|
...(node.maxLength === undefined ? {} : { maxLength: node.maxLength }),
|
|
84
93
|
...(node.pattern === undefined ? {} : { pattern: node.pattern }),
|