@cookbook/urlkit 1.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/LICENSE +21 -0
- package/README.md +707 -0
- package/dist/compile-path-wQfWAzOh.js +1318 -0
- package/dist/compile-path-wQfWAzOh.js.map +1 -0
- package/dist/compile-static-search-Cq3uaLe8.js +238 -0
- package/dist/compile-static-search-Cq3uaLe8.js.map +1 -0
- package/dist/contracts.d.ts +107 -0
- package/dist/create-url-contract-BYKPM9bn.js +1751 -0
- package/dist/create-url-contract-BYKPM9bn.js.map +1 -0
- package/dist/date/contracts.d.ts +5 -0
- package/dist/date/parse-custom-date.d.ts +7 -0
- package/dist/date/parse-date-time.d.ts +6 -0
- package/dist/date/parse-date.d.ts +6 -0
- package/dist/date/parse-unix-ms.d.ts +6 -0
- package/dist/date/parse-unix-seconds.d.ts +6 -0
- package/dist/date/serialize-custom-date.d.ts +7 -0
- package/dist/date/serialize-date-time.d.ts +6 -0
- package/dist/date/serialize-date.d.ts +6 -0
- package/dist/date/serialize-unix-ms.d.ts +6 -0
- package/dist/date/serialize-unix-seconds.d.ts +6 -0
- package/dist/errors/contracts.d.ts +5 -0
- package/dist/errors/url-kit-error.d.ts +8 -0
- package/dist/hash/build-hash.d.ts +4 -0
- package/dist/hash/compile-hash-descriptor.d.ts +2 -0
- package/dist/hash/compile-normalized-hash-descriptor.d.ts +2 -0
- package/dist/hash/compile-runtime-hash-descriptor.d.ts +3 -0
- package/dist/hash/compile-static-hash-descriptor.d.ts +3 -0
- package/dist/hash/contracts.d.ts +21 -0
- package/dist/hash/copy-normalized-hash-descriptor.d.ts +2 -0
- package/dist/hash/create-hash.d.ts +5 -0
- package/dist/hash/hash-fragment.d.ts +2 -0
- package/dist/hash/is-normalized-hash-descriptor.d.ts +2 -0
- package/dist/hash/normalize-hash.d.ts +3 -0
- package/dist/hash/parse-hash.d.ts +3 -0
- package/dist/hash/validate-normalized-hash-value.d.ts +5 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -0
- package/dist/router-runtime.d.ts +12 -0
- package/dist/router-runtime.js +214 -0
- package/dist/router-runtime.js.map +1 -0
- package/dist/runtime/build-route-search.d.ts +22 -0
- package/dist/runtime/compile-cached-static-search.d.ts +3 -0
- package/dist/runtime/contracts.d.ts +7 -0
- package/dist/runtime/create-route-url-contract.d.ts +15 -0
- package/dist/runtime/parse-route-search.d.ts +12 -0
- package/dist/schema/array.d.ts +9 -0
- package/dist/schema/boolean.d.ts +4 -0
- package/dist/schema/compile-runtime-schema-value.d.ts +6 -0
- package/dist/schema/compile-runtime-schema.d.ts +2 -0
- package/dist/schema/contracts.d.ts +87 -0
- package/dist/schema/create-schema-builder.d.ts +2 -0
- package/dist/schema/create-schema-value-error.d.ts +3 -0
- package/dist/schema/date-time.d.ts +4 -0
- package/dist/schema/date.d.ts +14 -0
- package/dist/schema/enum-of.d.ts +7 -0
- package/dist/schema/get-runtime-schema-internals.d.ts +2 -0
- package/dist/schema/handle-runtime-schema-absence.d.ts +6 -0
- package/dist/schema/int.d.ts +4 -0
- package/dist/schema/is-runtime-schema-kind.d.ts +2 -0
- package/dist/schema/normalize-compiled-runtime-schema-value.d.ts +3 -0
- package/dist/schema/normalize-runtime-schema-value.d.ts +2 -0
- package/dist/schema/number.d.ts +4 -0
- package/dist/schema/object.d.ts +20 -0
- package/dist/schema/parse-compiled-runtime-schema-value.d.ts +3 -0
- package/dist/schema/parse-runtime-schema-value.d.ts +2 -0
- package/dist/schema/runtime-schema-symbol.d.ts +5 -0
- package/dist/schema/runtime-schema-value-context.d.ts +2 -0
- package/dist/schema/safe-runtime-schema-value.d.ts +4 -0
- package/dist/schema/serialize-compiled-runtime-schema-value.d.ts +3 -0
- package/dist/schema/serialize-runtime-schema-value.d.ts +2 -0
- package/dist/schema/string.d.ts +4 -0
- package/dist/search/append-object-search-entries.d.ts +4 -0
- package/dist/search/append-raw-search-value.d.ts +2 -0
- package/dist/search/append-search-entry.d.ts +3 -0
- package/dist/search/are-search-values-equal.d.ts +1 -0
- package/dist/search/assert-object-search-collisions.d.ts +2 -0
- package/dist/search/build-compiled-search.d.ts +3 -0
- package/dist/search/build-raw-search.d.ts +2 -0
- package/dist/search/build-schema-search.d.ts +3 -0
- package/dist/search/build-search.d.ts +6 -0
- package/dist/search/collect-object-search-paths.d.ts +2 -0
- package/dist/search/compile-search-schema.d.ts +2 -0
- package/dist/search/contracts.d.ts +73 -0
- package/dist/search/copy-raw-search-params.d.ts +2 -0
- package/dist/search/copy-unknown-structured-search.d.ts +2 -0
- package/dist/search/create-search-params.d.ts +1 -0
- package/dist/search/create-search.d.ts +4 -0
- package/dist/search/delete-search-field-raw-keys.d.ts +2 -0
- package/dist/search/filter-raw-search.d.ts +3 -0
- package/dist/search/find-object-search-raw-value.d.ts +2 -0
- package/dist/search/has-search-field-raw-value.d.ts +2 -0
- package/dist/search/is-runtime-search-field.d.ts +2 -0
- package/dist/search/join-search-strings.d.ts +1 -0
- package/dist/search/normalize-compiled-search.d.ts +3 -0
- package/dist/search/normalize-search-build-value.d.ts +2 -0
- package/dist/search/normalize-search-field-default.d.ts +2 -0
- package/dist/search/normalize-search-field-type.d.ts +2 -0
- package/dist/search/object-search-key.d.ts +4 -0
- package/dist/search/object-search-path-key.d.ts +2 -0
- package/dist/search/object-search-raw-key-path.d.ts +1 -0
- package/dist/search/omit-search.d.ts +1 -0
- package/dist/search/parse-array-search-value.d.ts +4 -0
- package/dist/search/parse-compiled-search.d.ts +2 -0
- package/dist/search/parse-object-search-value.d.ts +4 -0
- package/dist/search/parse-partial-compiled-search.d.ts +6 -0
- package/dist/search/parse-partial-schema-search.d.ts +6 -0
- package/dist/search/parse-raw-search.d.ts +2 -0
- package/dist/search/parse-search-field-value.d.ts +2 -0
- package/dist/search/parse-search.d.ts +5 -0
- package/dist/search/patch-search.d.ts +6 -0
- package/dist/search/pick-search.d.ts +1 -0
- package/dist/search/replace-search.d.ts +5 -0
- package/dist/search/search-array-format.d.ts +3 -0
- package/dist/search/search-entries.d.ts +4 -0
- package/dist/search/serialize-search-build-value.d.ts +2 -0
- package/dist/search/serialize-search-entries.d.ts +3 -0
- package/dist/static/compile-static-hash.d.ts +3 -0
- package/dist/static/compile-static-search.d.ts +3 -0
- package/dist/static/compile-static-url.d.ts +3 -0
- package/dist/static/contracts.d.ts +81 -0
- package/dist/static/create-static-search-schema.d.ts +3 -0
- package/dist/static/normalize-static-search-default.d.ts +2 -0
- package/dist/static/static-search-field-kind.d.ts +4 -0
- package/dist/static.d.ts +7 -0
- package/dist/static.js +55 -0
- package/dist/static.js.map +1 -0
- package/dist/url/assert-path-match-failure.d.ts +2 -0
- package/dist/url/build-compiled-url.d.ts +3 -0
- package/dist/url/build-url.d.ts +3 -0
- package/dist/url/coerce-path-param.d.ts +2 -0
- package/dist/url/compile-path.d.ts +2 -0
- package/dist/url/compile-runtime-url-descriptor.d.ts +2 -0
- package/dist/url/compile-url-descriptor.d.ts +13 -0
- package/dist/url/contracts.d.ts +91 -0
- package/dist/url/create-unsupported-url-method.d.ts +1 -0
- package/dist/url/create-url-contract.d.ts +3 -0
- package/dist/url/create-url.d.ts +2 -0
- package/dist/url/filter-compiled-url-search.d.ts +4 -0
- package/dist/url/format-parsed-url.d.ts +2 -0
- package/dist/url/match-url.d.ts +3 -0
- package/dist/url/normalize-compiled-url.d.ts +3 -0
- package/dist/url/normalize-path-build-params.d.ts +1 -0
- package/dist/url/normalize-url.d.ts +3 -0
- package/dist/url/parse-compiled-url.d.ts +3 -0
- package/dist/url/parse-path-pattern.d.ts +2 -0
- package/dist/url/parse-request.d.ts +2 -0
- package/dist/url/parse-url.d.ts +6 -0
- package/dist/url/patch-compiled-url-search.d.ts +3 -0
- package/dist/url/path-constraints.d.ts +5 -0
- package/dist/url/path-param-kind.d.ts +3 -0
- package/dist/url/path-segment.d.ts +11 -0
- package/dist/url/register-urlkit-path-constraints.d.ts +1 -0
- package/dist/url/replace-compiled-url-search.d.ts +3 -0
- package/dist/url/resolve-url-unknown-search.d.ts +3 -0
- package/dist/url/url-state-brand.d.ts +7 -0
- package/package.json +73 -0
|
@@ -0,0 +1,1751 @@
|
|
|
1
|
+
import { g as handleRuntimeSchemaAbsence, j as createSchemaValueError, U as UrlKitError, k as createRuntimeSchemaValueContext, l as compileRuntimeSchemaValue, m as createRuntimeSchemaBuilder, o as normalizeRuntimeSchemaValue, p as getRuntimeSchemaInternals, q as runtimeSchemaSymbol, t as compileRuntimeSchema, u as normalizeCompiledRuntimeSchemaValue, v as compileStaticHashDescriptor, c as compileSearchSchema, a as compilePath } from './compile-path-wQfWAzOh.js';
|
|
2
|
+
|
|
3
|
+
function parseCompiledRuntimeSchemaValue(compiled, input, options = {}) {
|
|
4
|
+
const absence = handleRuntimeSchemaAbsence(compiled.descriptor, input, options);
|
|
5
|
+
if (absence.handled) {
|
|
6
|
+
return absence.value;
|
|
7
|
+
}
|
|
8
|
+
if (typeof input !== 'string') {
|
|
9
|
+
throw createSchemaValueError(options.errorCode ?? 'invalid-search', 'Serialized schema values must be strings.', options.path ?? []);
|
|
10
|
+
}
|
|
11
|
+
if (!compiled.codec) {
|
|
12
|
+
throw new UrlKitError('invalid-descriptor', `Runtime schema "${compiled.descriptor.kind}" does not define a parser.`, options.path ? { path: options.path } : undefined);
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
return compiled.codec.parse(input, createRuntimeSchemaValueContext(compiled.descriptor.kind, options));
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (error instanceof UrlKitError) {
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
throw createSchemaValueError(options.errorCode ?? 'invalid-search', 'Schema value is invalid.', options.path ?? [], error);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function parseRuntimeSchemaValue(schema, input, options = {}) {
|
|
26
|
+
return parseCompiledRuntimeSchemaValue(compileRuntimeSchemaValue(schema, options), input, options);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function serializeCompiledRuntimeSchemaValue(compiled, input, options = {}) {
|
|
30
|
+
const absence = handleRuntimeSchemaAbsence(compiled.descriptor, input, options);
|
|
31
|
+
if (absence.handled) {
|
|
32
|
+
if (absence.value === undefined) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
input = absence.value;
|
|
36
|
+
}
|
|
37
|
+
if (!compiled.codec) {
|
|
38
|
+
throw new UrlKitError('invalid-descriptor', `Runtime schema "${compiled.descriptor.kind}" does not define a serializer.`, options.path ? { path: options.path } : undefined);
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const context = createRuntimeSchemaValueContext(compiled.descriptor.kind, options);
|
|
42
|
+
const normalized = compiled.codec.normalize(input, context);
|
|
43
|
+
return compiled.codec.serialize(normalized, context);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
if (error instanceof UrlKitError) {
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
throw createSchemaValueError(options.errorCode ?? 'invalid-search', 'Schema value is invalid.', options.path ?? [], error);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function serializeRuntimeSchemaValue(schema, input, options = {}) {
|
|
54
|
+
return serializeCompiledRuntimeSchemaValue(compileRuntimeSchemaValue(schema, options), input, options);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const arrayElements = new WeakMap();
|
|
58
|
+
function array(element) {
|
|
59
|
+
const elementDescriptor = createElementDescriptor(element);
|
|
60
|
+
const builder = createRuntimeSchemaBuilder({
|
|
61
|
+
kind: 'array',
|
|
62
|
+
options: {
|
|
63
|
+
element: elementDescriptor,
|
|
64
|
+
},
|
|
65
|
+
codec: createArrayCodec(element),
|
|
66
|
+
validateDescriptor(context) {
|
|
67
|
+
compileRuntimeSchema(element, { path: [...context.path, '*'] });
|
|
68
|
+
},
|
|
69
|
+
validateDefault(value, context) {
|
|
70
|
+
validateArrayDefault(element, value, context);
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
return withArrayElement(builder, element);
|
|
74
|
+
}
|
|
75
|
+
function parseArrayRuntimeSchemaValue(schema, input, context) {
|
|
76
|
+
const values = Array.isArray(input) ? input : [input];
|
|
77
|
+
const element = getArrayElementSchema(schema);
|
|
78
|
+
return Object.freeze(values.map((value) => parseRuntimeSchemaValue(element, value, {
|
|
79
|
+
path: context.path,
|
|
80
|
+
errorCode: context.errorCode,
|
|
81
|
+
missingCode: context.errorCode,
|
|
82
|
+
})));
|
|
83
|
+
}
|
|
84
|
+
function serializeArrayRuntimeSchemaValue(schema, input, context) {
|
|
85
|
+
const normalized = normalizeRuntimeSchemaValue(schema, input, {
|
|
86
|
+
path: context.path,
|
|
87
|
+
errorCode: context.errorCode,
|
|
88
|
+
missingCode: context.errorCode,
|
|
89
|
+
});
|
|
90
|
+
if (!normalized.length) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
const element = getArrayElementSchema(schema);
|
|
94
|
+
return Object.freeze(normalized.map((value) => serializeRuntimeSchemaValue(element, value, {
|
|
95
|
+
path: context.path,
|
|
96
|
+
errorCode: context.errorCode,
|
|
97
|
+
missingCode: context.errorCode,
|
|
98
|
+
}) ?? ''));
|
|
99
|
+
}
|
|
100
|
+
function withArrayElement(builder, element) {
|
|
101
|
+
const wrapped = {
|
|
102
|
+
[runtimeSchemaSymbol]: builder[runtimeSchemaSymbol],
|
|
103
|
+
optional: () => withArrayElement(builder.optional(), element),
|
|
104
|
+
required: () => withArrayElement(builder.required(), element),
|
|
105
|
+
default: (value) => withArrayElement(builder.default(value), element),
|
|
106
|
+
};
|
|
107
|
+
arrayElements.set(wrapped, element);
|
|
108
|
+
return Object.freeze(wrapped);
|
|
109
|
+
}
|
|
110
|
+
function createArrayCodec(schema) {
|
|
111
|
+
return {
|
|
112
|
+
parse(input, context) {
|
|
113
|
+
return Object.freeze([
|
|
114
|
+
parseRuntimeSchemaValue(schema, input, {
|
|
115
|
+
path: context.path,
|
|
116
|
+
errorCode: context.errorCode,
|
|
117
|
+
missingCode: context.errorCode,
|
|
118
|
+
}),
|
|
119
|
+
]);
|
|
120
|
+
},
|
|
121
|
+
normalize(input, context) {
|
|
122
|
+
if (!Array.isArray(input)) {
|
|
123
|
+
throw createSchemaValueError(context.errorCode, 'Expected an array value.', context.path);
|
|
124
|
+
}
|
|
125
|
+
return Object.freeze(input.map((item) => normalizeRuntimeSchemaValue(schema, item, {
|
|
126
|
+
path: context.path,
|
|
127
|
+
errorCode: context.errorCode,
|
|
128
|
+
missingCode: context.errorCode,
|
|
129
|
+
})));
|
|
130
|
+
},
|
|
131
|
+
serialize() {
|
|
132
|
+
throw new UrlKitError('invalid-descriptor', 'Array schemas require repeated-key search serialization.');
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function validateArrayDefault(schema, value, context) {
|
|
137
|
+
if (!Array.isArray(value)) {
|
|
138
|
+
throw new UrlKitError('invalid-descriptor', 'Array schema default must be an array.', {
|
|
139
|
+
path: context.path,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
for (const item of value) {
|
|
143
|
+
normalizeRuntimeSchemaValue(schema, item, {
|
|
144
|
+
path: context.path,
|
|
145
|
+
errorCode: 'invalid-descriptor',
|
|
146
|
+
missingCode: 'invalid-descriptor',
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function createElementDescriptor(schema) {
|
|
151
|
+
return getRuntimeSchemaInternals(schema).toDescriptor();
|
|
152
|
+
}
|
|
153
|
+
function getArrayElementSchema(schema) {
|
|
154
|
+
const element = arrayElements.get(schema);
|
|
155
|
+
if (element) {
|
|
156
|
+
return element;
|
|
157
|
+
}
|
|
158
|
+
throw new UrlKitError('invalid-descriptor', 'Array schema element is missing.');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const objectShapes = new WeakMap();
|
|
162
|
+
function object(shape) {
|
|
163
|
+
validateObjectShape(shape);
|
|
164
|
+
const builder = createRuntimeSchemaBuilder({
|
|
165
|
+
kind: 'object',
|
|
166
|
+
options: {
|
|
167
|
+
shape: createShapeDescriptor(shape),
|
|
168
|
+
},
|
|
169
|
+
codec: createObjectCodec(shape),
|
|
170
|
+
validateDescriptor(context) {
|
|
171
|
+
validateObjectDescriptor(shape, context);
|
|
172
|
+
},
|
|
173
|
+
validateDefault(value, context) {
|
|
174
|
+
validateObjectDefault(shape, value, context);
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
return withObjectShape(builder, shape);
|
|
178
|
+
}
|
|
179
|
+
function withObjectShape(builder, shape) {
|
|
180
|
+
const wrapped = {
|
|
181
|
+
[runtimeSchemaSymbol]: builder[runtimeSchemaSymbol],
|
|
182
|
+
optional: () => withObjectShape(builder.optional(), shape),
|
|
183
|
+
required: () => withObjectShape(builder.required(), shape),
|
|
184
|
+
default: (value) => withObjectShape(builder.default(value), shape),
|
|
185
|
+
};
|
|
186
|
+
objectShapes.set(wrapped, shape);
|
|
187
|
+
return Object.freeze(wrapped);
|
|
188
|
+
}
|
|
189
|
+
function getObjectSchemaShape(schema) {
|
|
190
|
+
const shape = objectShapes.get(schema);
|
|
191
|
+
if (shape) {
|
|
192
|
+
return shape;
|
|
193
|
+
}
|
|
194
|
+
throw new UrlKitError('invalid-descriptor', 'Object schema shape is missing.');
|
|
195
|
+
}
|
|
196
|
+
function createObjectCodec(shape) {
|
|
197
|
+
return {
|
|
198
|
+
parse(_input, context) {
|
|
199
|
+
throw createSchemaValueError(context.errorCode, 'Object search values must use declared dotted keys.', context.path);
|
|
200
|
+
},
|
|
201
|
+
normalize(input, context) {
|
|
202
|
+
return normalizeObjectValue(shape, input, context);
|
|
203
|
+
},
|
|
204
|
+
serialize(_input, context) {
|
|
205
|
+
throw createSchemaValueError(context.errorCode, 'Object search values must use declared dotted keys.', context.path);
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function normalizeObjectValue(shape, input, context) {
|
|
210
|
+
if (!isPlainObject$3(input)) {
|
|
211
|
+
throw createSchemaValueError(context.errorCode, 'Expected an object value.', context.path);
|
|
212
|
+
}
|
|
213
|
+
const result = {};
|
|
214
|
+
for (const [key, schema] of Object.entries(shape)) {
|
|
215
|
+
const value = normalizeRuntimeSchemaValue(schema, input[key], {
|
|
216
|
+
path: [...context.path, key],
|
|
217
|
+
errorCode: context.errorCode,
|
|
218
|
+
missingCode: context.errorCode,
|
|
219
|
+
});
|
|
220
|
+
if (value !== undefined) {
|
|
221
|
+
result[key] = value;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return Object.freeze(result);
|
|
225
|
+
}
|
|
226
|
+
function validateObjectDefault(shape, value, context) {
|
|
227
|
+
normalizeObjectValue(shape, value, {
|
|
228
|
+
path: context.path,
|
|
229
|
+
errorCode: 'invalid-descriptor',
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
function validateObjectDescriptor(shape, context) {
|
|
233
|
+
for (const [key, schema] of Object.entries(shape)) {
|
|
234
|
+
compileRuntimeSchema(schema, { path: [...context.path, key] });
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function createShapeDescriptor(shape) {
|
|
238
|
+
const descriptors = {};
|
|
239
|
+
for (const [key, schema] of Object.entries(shape)) {
|
|
240
|
+
descriptors[key] = getRuntimeSchemaInternals(schema).toDescriptor();
|
|
241
|
+
}
|
|
242
|
+
return Object.freeze(descriptors);
|
|
243
|
+
}
|
|
244
|
+
function validateObjectShape(shape) {
|
|
245
|
+
if (!isPlainObject$3(shape)) {
|
|
246
|
+
throw new UrlKitError('invalid-descriptor', 'Object schema shape must be an object.');
|
|
247
|
+
}
|
|
248
|
+
for (const [key, schema] of Object.entries(shape)) {
|
|
249
|
+
if (!key) {
|
|
250
|
+
throw new UrlKitError('invalid-descriptor', 'Object schema field names must not be empty.');
|
|
251
|
+
}
|
|
252
|
+
getRuntimeSchemaInternals(schema);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function isPlainObject$3(input) {
|
|
256
|
+
return (typeof input === 'object' && input !== null && !Array.isArray(input) && !(input instanceof Date));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function copyNormalizedHashDescriptor(descriptor) {
|
|
260
|
+
const values = descriptor.values ? Object.freeze([...descriptor.values]) : undefined;
|
|
261
|
+
if (descriptor.presence === 'defaulted') {
|
|
262
|
+
return Object.freeze({
|
|
263
|
+
kind: descriptor.kind,
|
|
264
|
+
presence: 'defaulted',
|
|
265
|
+
...(values ? { values } : {}),
|
|
266
|
+
defaultValue: descriptor.defaultValue,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
return Object.freeze({
|
|
270
|
+
kind: descriptor.kind,
|
|
271
|
+
presence: descriptor.presence,
|
|
272
|
+
...(values ? { values } : {}),
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const HASH_PATH$2 = Object.freeze(['hash']);
|
|
277
|
+
function validateNormalizedHashValue(descriptor, input, options) {
|
|
278
|
+
if (input === undefined || input === null) {
|
|
279
|
+
if (descriptor.presence === 'optional') {
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
if (descriptor.presence === 'defaulted') {
|
|
283
|
+
return descriptor.defaultValue;
|
|
284
|
+
}
|
|
285
|
+
throw new UrlKitError('invalid-hash', input === null ? 'Required hash cannot be null.' : 'Required hash is missing.', {
|
|
286
|
+
path: HASH_PATH$2,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
if (typeof input !== 'string') {
|
|
290
|
+
throw new UrlKitError('invalid-hash', options.serialized ? 'Serialized hash must be a string.' : 'Hash must be a string.', { path: HASH_PATH$2 });
|
|
291
|
+
}
|
|
292
|
+
if (descriptor.kind === 'enum' && !(descriptor.values ?? []).includes(input)) {
|
|
293
|
+
throw new UrlKitError('invalid-hash', `Expected hash to be one of: ${(descriptor.values ?? []).join(', ')}.`, {
|
|
294
|
+
path: HASH_PATH$2,
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
return input;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function compileNormalizedHashDescriptor(descriptor) {
|
|
301
|
+
const normalizedDescriptor = copyNormalizedHashDescriptor(descriptor);
|
|
302
|
+
return Object.freeze({
|
|
303
|
+
descriptor: normalizedDescriptor,
|
|
304
|
+
parse(input) {
|
|
305
|
+
return validateNormalizedHashValue(normalizedDescriptor, input, { serialized: true });
|
|
306
|
+
},
|
|
307
|
+
normalize(input) {
|
|
308
|
+
return validateNormalizedHashValue(normalizedDescriptor, input, { serialized: false });
|
|
309
|
+
},
|
|
310
|
+
serialize(input) {
|
|
311
|
+
const value = validateNormalizedHashValue(normalizedDescriptor, input, { serialized: false });
|
|
312
|
+
return value ?? undefined;
|
|
313
|
+
},
|
|
314
|
+
isDefault(input) {
|
|
315
|
+
if (normalizedDescriptor.presence !== 'defaulted') {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
return (validateNormalizedHashValue(normalizedDescriptor, input, { serialized: false }) ===
|
|
319
|
+
normalizedDescriptor.defaultValue);
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const HASH_PATH$1 = Object.freeze(['hash']);
|
|
325
|
+
function compileRuntimeHashDescriptor(schema) {
|
|
326
|
+
const compiledSchema = compileRuntimeSchemaValue(schema, { path: HASH_PATH$1 });
|
|
327
|
+
const descriptor = compiledSchema.descriptor;
|
|
328
|
+
if (descriptor.kind !== 'string' && descriptor.kind !== 'enum') {
|
|
329
|
+
throw new UrlKitError('invalid-descriptor', 'Hash schema must be a string or enum schema.', {
|
|
330
|
+
path: HASH_PATH$1,
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
return Object.freeze({
|
|
334
|
+
descriptor: toNormalizedHashDescriptor(descriptor),
|
|
335
|
+
parse(input) {
|
|
336
|
+
return parseCompiledRuntimeSchemaValue(compiledSchema, input, {
|
|
337
|
+
path: HASH_PATH$1,
|
|
338
|
+
errorCode: 'invalid-hash',
|
|
339
|
+
missingCode: 'invalid-hash',
|
|
340
|
+
});
|
|
341
|
+
},
|
|
342
|
+
normalize(input) {
|
|
343
|
+
return normalizeCompiledRuntimeSchemaValue(compiledSchema, input, {
|
|
344
|
+
path: HASH_PATH$1,
|
|
345
|
+
errorCode: 'invalid-hash',
|
|
346
|
+
missingCode: 'invalid-hash',
|
|
347
|
+
});
|
|
348
|
+
},
|
|
349
|
+
serialize(input) {
|
|
350
|
+
return serializeCompiledRuntimeSchemaValue(compiledSchema, input, {
|
|
351
|
+
path: HASH_PATH$1,
|
|
352
|
+
errorCode: 'invalid-hash',
|
|
353
|
+
missingCode: 'invalid-hash',
|
|
354
|
+
});
|
|
355
|
+
},
|
|
356
|
+
isDefault(input) {
|
|
357
|
+
if (descriptor.presence !== 'defaulted') {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
return (normalizeCompiledRuntimeSchemaValue(compiledSchema, input, {
|
|
361
|
+
path: HASH_PATH$1,
|
|
362
|
+
errorCode: 'invalid-hash',
|
|
363
|
+
missingCode: 'invalid-hash',
|
|
364
|
+
}) === descriptor.defaultValue);
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
function toNormalizedHashDescriptor(descriptor) {
|
|
369
|
+
const values = isStringArray(descriptor.options.values)
|
|
370
|
+
? [...descriptor.options.values]
|
|
371
|
+
: undefined;
|
|
372
|
+
if (descriptor.presence === 'defaulted') {
|
|
373
|
+
return Object.freeze({
|
|
374
|
+
kind: descriptor.kind,
|
|
375
|
+
presence: 'defaulted',
|
|
376
|
+
...(values ? { values: Object.freeze(values) } : {}),
|
|
377
|
+
defaultValue: descriptor.defaultValue,
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
return Object.freeze({
|
|
381
|
+
kind: descriptor.kind,
|
|
382
|
+
presence: descriptor.presence,
|
|
383
|
+
...(values ? { values: Object.freeze(values) } : {}),
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
function isStringArray(input) {
|
|
387
|
+
return Array.isArray(input) && input.every((value) => typeof value === 'string');
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function isNormalizedHashDescriptor(input) {
|
|
391
|
+
return (isRecord$4(input) &&
|
|
392
|
+
(input.kind === 'string' || input.kind === 'enum') &&
|
|
393
|
+
(input.presence === 'required' ||
|
|
394
|
+
input.presence === 'optional' ||
|
|
395
|
+
input.presence === 'defaulted'));
|
|
396
|
+
}
|
|
397
|
+
function isRecord$4(input) {
|
|
398
|
+
return typeof input === 'object' && input !== null && !Array.isArray(input);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const HASH_PATH = Object.freeze(['hash']);
|
|
402
|
+
function compileHashDescriptor(descriptor) {
|
|
403
|
+
if (descriptor === undefined) {
|
|
404
|
+
return compileNormalizedHashDescriptor({ kind: 'string', presence: 'optional' });
|
|
405
|
+
}
|
|
406
|
+
const runtimeKind = getRuntimeSchemaKind(descriptor);
|
|
407
|
+
if (runtimeKind) {
|
|
408
|
+
if (runtimeKind !== 'string' && runtimeKind !== 'enum') {
|
|
409
|
+
throw new UrlKitError('invalid-descriptor', 'Hash schema must be a string or enum schema.', {
|
|
410
|
+
path: HASH_PATH,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
return compileRuntimeHashDescriptor(descriptor);
|
|
414
|
+
}
|
|
415
|
+
if (isNormalizedHashDescriptor(descriptor)) {
|
|
416
|
+
return compileNormalizedHashDescriptor(descriptor);
|
|
417
|
+
}
|
|
418
|
+
return compileNormalizedHashDescriptor(compileStaticHashDescriptor(descriptor));
|
|
419
|
+
}
|
|
420
|
+
function getRuntimeSchemaKind(input) {
|
|
421
|
+
try {
|
|
422
|
+
const internals = getRuntimeSchemaInternals(input);
|
|
423
|
+
return internals.kind;
|
|
424
|
+
}
|
|
425
|
+
catch {
|
|
426
|
+
return undefined;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function readHashFragment(input) {
|
|
431
|
+
if (input === undefined || input === null) {
|
|
432
|
+
return undefined;
|
|
433
|
+
}
|
|
434
|
+
if (input instanceof URL) {
|
|
435
|
+
return readHashFragment(input.hash);
|
|
436
|
+
}
|
|
437
|
+
if (typeof input !== 'string') {
|
|
438
|
+
throw new UrlKitError('invalid-hash', 'Hash must be a string.');
|
|
439
|
+
}
|
|
440
|
+
const withoutPrefix = input.startsWith('#') ? input.slice(1) : input;
|
|
441
|
+
if (!withoutPrefix) {
|
|
442
|
+
return undefined;
|
|
443
|
+
}
|
|
444
|
+
try {
|
|
445
|
+
return decodeURIComponent(withoutPrefix);
|
|
446
|
+
}
|
|
447
|
+
catch (error) {
|
|
448
|
+
throw new UrlKitError('invalid-hash', 'Hash is not valid URL encoding.', {
|
|
449
|
+
path: ['hash'],
|
|
450
|
+
cause: error,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
function writeHashFragment(input) {
|
|
455
|
+
return `#${encodeURIComponent(input)}`;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function buildHash(hash, descriptorOrOptions, options) {
|
|
459
|
+
const descriptor = isBuildUrlOptions(descriptorOrOptions) ? undefined : descriptorOrOptions;
|
|
460
|
+
const buildOptions = isBuildUrlOptions(descriptorOrOptions) ? descriptorOrOptions : options;
|
|
461
|
+
const compiled = compileHashDescriptor(descriptor);
|
|
462
|
+
if (buildOptions?.defaults === 'omit' &&
|
|
463
|
+
hash !== undefined &&
|
|
464
|
+
hash !== null &&
|
|
465
|
+
compiled.isDefault(hash)) {
|
|
466
|
+
return '';
|
|
467
|
+
}
|
|
468
|
+
if (buildOptions?.defaults === 'omit' &&
|
|
469
|
+
(hash === undefined || hash === null) &&
|
|
470
|
+
compiled.descriptor.presence !== 'required') {
|
|
471
|
+
return '';
|
|
472
|
+
}
|
|
473
|
+
const value = compiled.serialize(hash);
|
|
474
|
+
return value === undefined ? '' : writeHashFragment(value);
|
|
475
|
+
}
|
|
476
|
+
function isBuildUrlOptions(input) {
|
|
477
|
+
return (typeof input === 'object' && input !== null && !Array.isArray(input) && 'defaults' in input);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function parseHash(input, descriptor) {
|
|
481
|
+
const hash = readHashFragment(input);
|
|
482
|
+
if (descriptor === undefined) {
|
|
483
|
+
return hash;
|
|
484
|
+
}
|
|
485
|
+
return compileHashDescriptor(descriptor).parse(hash);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function isRuntimeSchemaKind(schema, kind) {
|
|
489
|
+
return getRuntimeSchemaInternals(schema).kind === kind;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function appendSearchEntry(entries, key, value, options = {}) {
|
|
493
|
+
if (value === undefined) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
if (typeof value === 'string') {
|
|
497
|
+
entries.push({ key, value });
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
appendArraySearchEntry(entries, key, value, options);
|
|
501
|
+
}
|
|
502
|
+
function appendArraySearchEntry(entries, key, values, options) {
|
|
503
|
+
if (!values.length) {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (options.arrayFormat === 'comma') {
|
|
507
|
+
entries.push({ key, value: values.join(',') });
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
for (const value of values) {
|
|
511
|
+
entries.push({ key, value });
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function joinObjectSearchKey(parentKey, childKey) {
|
|
516
|
+
return `${parentKey}.${escapeObjectSearchSegment(childKey)}`;
|
|
517
|
+
}
|
|
518
|
+
function escapeObjectSearchSegment(segment) {
|
|
519
|
+
return segment.replaceAll('~', '~0').replaceAll('.', '~1');
|
|
520
|
+
}
|
|
521
|
+
function unescapeObjectSearchSegment(segment) {
|
|
522
|
+
let output = '';
|
|
523
|
+
for (let index = 0; index < segment.length; index += 1) {
|
|
524
|
+
const character = segment[index];
|
|
525
|
+
if (character !== '~') {
|
|
526
|
+
output += character ?? '';
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
const escapeCode = segment[index + 1];
|
|
530
|
+
if (escapeCode === '0') {
|
|
531
|
+
output += '~';
|
|
532
|
+
index += 1;
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
if (escapeCode === '1') {
|
|
536
|
+
output += '.';
|
|
537
|
+
index += 1;
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
output += character ?? '';
|
|
541
|
+
}
|
|
542
|
+
return output;
|
|
543
|
+
}
|
|
544
|
+
function splitObjectSearchKey(key) {
|
|
545
|
+
return Object.freeze(key.split('.').map(unescapeObjectSearchSegment));
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function appendObjectSearchEntries(entries, parentKey, schema, value, options = {}) {
|
|
549
|
+
if (!isPlainObject$2(value)) {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
const shape = getObjectSchemaShape(schema);
|
|
553
|
+
for (const [key, childSchema] of Object.entries(shape)) {
|
|
554
|
+
const childValue = value[key];
|
|
555
|
+
if (childValue === undefined) {
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
const childKey = joinObjectSearchKey(parentKey, key);
|
|
559
|
+
appendObjectChildSearchEntry(entries, childKey, childSchema, childValue, options);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
function appendObjectChildSearchEntry(entries, key, schema, value, options) {
|
|
563
|
+
if (isRuntimeSchemaKind(schema, 'object')) {
|
|
564
|
+
appendObjectSearchEntries(entries, key, schema, value, options);
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
if (isRuntimeSchemaKind(schema, 'array')) {
|
|
568
|
+
appendSearchEntry(entries, key, serializeArrayRuntimeSchemaValue(schema, value, {
|
|
569
|
+
path: [key],
|
|
570
|
+
errorCode: 'invalid-search',
|
|
571
|
+
}), options);
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
appendSearchEntry(entries, key, serializeRuntimeSchemaValue(schema, value, {
|
|
575
|
+
path: [key],
|
|
576
|
+
errorCode: 'invalid-search',
|
|
577
|
+
missingCode: 'missing-search',
|
|
578
|
+
}), options);
|
|
579
|
+
}
|
|
580
|
+
function isPlainObject$2(input) {
|
|
581
|
+
return (typeof input === 'object' && input !== null && !Array.isArray(input) && !(input instanceof Date));
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function areSearchValuesEqual(left, right) {
|
|
585
|
+
if (left instanceof Date || right instanceof Date) {
|
|
586
|
+
return left instanceof Date && right instanceof Date && left.getTime() === right.getTime();
|
|
587
|
+
}
|
|
588
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
589
|
+
return Array.isArray(left) && Array.isArray(right) && areArraysEqual(left, right);
|
|
590
|
+
}
|
|
591
|
+
if (isPlainObject$1(left) || isPlainObject$1(right)) {
|
|
592
|
+
return isPlainObject$1(left) && isPlainObject$1(right) && areObjectsEqual(left, right);
|
|
593
|
+
}
|
|
594
|
+
return left === right;
|
|
595
|
+
}
|
|
596
|
+
function areArraysEqual(left, right) {
|
|
597
|
+
if (left.length !== right.length) {
|
|
598
|
+
return false;
|
|
599
|
+
}
|
|
600
|
+
return left.every((value, index) => areSearchValuesEqual(value, right[index]));
|
|
601
|
+
}
|
|
602
|
+
function areObjectsEqual(left, right) {
|
|
603
|
+
const leftKeys = Object.keys(left);
|
|
604
|
+
const rightKeys = Object.keys(right);
|
|
605
|
+
if (leftKeys.length !== rightKeys.length) {
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
return leftKeys.every((key) => Object.prototype.hasOwnProperty.call(right, key) &&
|
|
609
|
+
areSearchValuesEqual(left[key], right[key]));
|
|
610
|
+
}
|
|
611
|
+
function isPlainObject$1(input) {
|
|
612
|
+
return (typeof input === 'object' && input !== null && !Array.isArray(input) && !(input instanceof Date));
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function normalizeSearchBuildValue(field, input) {
|
|
616
|
+
if (isRuntimeSchemaKind(field.schema, 'array') || isRuntimeSchemaKind(field.schema, 'object')) {
|
|
617
|
+
return normalizeCompiledRuntimeSchemaValue(field.compiledSchema, input, {
|
|
618
|
+
path: [field.key],
|
|
619
|
+
errorCode: 'invalid-search',
|
|
620
|
+
missingCode: 'missing-search',
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
if (field.type === 'many') {
|
|
624
|
+
return normalizeManySearchBuildValue(field, input);
|
|
625
|
+
}
|
|
626
|
+
return normalizeOneSearchBuildValue(field, input);
|
|
627
|
+
}
|
|
628
|
+
function normalizeOneSearchBuildValue(field, input) {
|
|
629
|
+
if (input === undefined || input === null) {
|
|
630
|
+
if (field.presence === 'optional') {
|
|
631
|
+
return undefined;
|
|
632
|
+
}
|
|
633
|
+
if (field.presence === 'defaulted') {
|
|
634
|
+
return field.defaultValue;
|
|
635
|
+
}
|
|
636
|
+
if (input === null) {
|
|
637
|
+
throw new UrlKitError('invalid-search', 'Required search parameter cannot be null.', {
|
|
638
|
+
path: [field.key],
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
throw new UrlKitError('missing-search', 'Required search parameter is missing.', {
|
|
642
|
+
path: [field.key],
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
return normalizeCompiledRuntimeSchemaValue(field.compiledSchema, input, {
|
|
646
|
+
path: [field.key],
|
|
647
|
+
errorCode: 'invalid-search',
|
|
648
|
+
missingCode: 'missing-search',
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
function normalizeManySearchBuildValue(field, input) {
|
|
652
|
+
if (input === undefined || input === null) {
|
|
653
|
+
if (field.presence === 'optional') {
|
|
654
|
+
return undefined;
|
|
655
|
+
}
|
|
656
|
+
if (field.presence === 'defaulted') {
|
|
657
|
+
return copyArrayDefault(field);
|
|
658
|
+
}
|
|
659
|
+
if (input === null) {
|
|
660
|
+
throw new UrlKitError('invalid-search', 'Required search parameter cannot be null.', {
|
|
661
|
+
path: [field.key],
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
throw new UrlKitError('missing-search', 'Required search parameter is missing.', {
|
|
665
|
+
path: [field.key],
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
if (!Array.isArray(input)) {
|
|
669
|
+
throw new UrlKitError('invalid-search', 'Expected an array search parameter value.', {
|
|
670
|
+
path: [field.key],
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
return Object.freeze(input.map((item) => normalizeCompiledRuntimeSchemaValue(field.compiledSchema, item, {
|
|
674
|
+
path: [field.key],
|
|
675
|
+
errorCode: 'invalid-search',
|
|
676
|
+
missingCode: 'missing-search',
|
|
677
|
+
})));
|
|
678
|
+
}
|
|
679
|
+
function copyArrayDefault(field) {
|
|
680
|
+
const value = field.defaultValue;
|
|
681
|
+
if (!Array.isArray(value)) {
|
|
682
|
+
return Object.freeze([]);
|
|
683
|
+
}
|
|
684
|
+
return Object.freeze([...value]);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function serializeSearchBuildValue(field, normalized) {
|
|
688
|
+
if (isRuntimeSchemaKind(field.schema, 'array')) {
|
|
689
|
+
return serializeArrayRuntimeSchemaValue(field.schema, normalized, {
|
|
690
|
+
path: [field.key],
|
|
691
|
+
errorCode: 'invalid-search',
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
if (isRuntimeSchemaKind(field.schema, 'object')) {
|
|
695
|
+
return undefined;
|
|
696
|
+
}
|
|
697
|
+
if (field.type === 'many') {
|
|
698
|
+
if (!Array.isArray(normalized) || !normalized.length) {
|
|
699
|
+
return undefined;
|
|
700
|
+
}
|
|
701
|
+
return Object.freeze(normalized.map((item) => serializeOneSearchBuildValue(field, item)));
|
|
702
|
+
}
|
|
703
|
+
return serializeOneSearchBuildValue(field, normalized);
|
|
704
|
+
}
|
|
705
|
+
function serializeOneSearchBuildValue(field, value) {
|
|
706
|
+
const serialized = serializeCompiledRuntimeSchemaValue(field.compiledSchema, value, {
|
|
707
|
+
path: [field.key],
|
|
708
|
+
errorCode: 'invalid-search',
|
|
709
|
+
missingCode: 'missing-search',
|
|
710
|
+
});
|
|
711
|
+
return serialized ?? '';
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function serializeSearchEntries(entries, options = {}) {
|
|
715
|
+
if (!entries.length) {
|
|
716
|
+
return '';
|
|
717
|
+
}
|
|
718
|
+
const orderedEntries = options.sortKeys ? [...entries].sort(compareSearchEntryKeys) : entries;
|
|
719
|
+
const searchParams = new URLSearchParams();
|
|
720
|
+
for (const entry of orderedEntries) {
|
|
721
|
+
searchParams.append(entry.key, entry.value);
|
|
722
|
+
}
|
|
723
|
+
const search = searchParams.toString();
|
|
724
|
+
return search ? `?${search}` : '';
|
|
725
|
+
}
|
|
726
|
+
function compareSearchEntryKeys(left, right) {
|
|
727
|
+
return left.key.localeCompare(right.key);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
function buildCompiledSearch(input = {}, compiled, options = {}) {
|
|
731
|
+
const entries = [];
|
|
732
|
+
for (const field of compiled.fields) {
|
|
733
|
+
const normalized = normalizeSearchBuildValue(field, input[field.key]);
|
|
734
|
+
if (normalized === undefined || shouldOmitDefault(field.defaultValue, normalized, options)) {
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
if (isRuntimeSchemaKind(field.schema, 'object')) {
|
|
738
|
+
appendObjectSearchEntries(entries, field.key, field.schema, normalized, options);
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
appendSearchEntry(entries, field.key, serializeSearchBuildValue(field, normalized), options);
|
|
742
|
+
}
|
|
743
|
+
return serializeSearchEntries(entries, options);
|
|
744
|
+
}
|
|
745
|
+
function shouldOmitDefault(defaultValue, normalized, options) {
|
|
746
|
+
return (options.defaults === 'omit' &&
|
|
747
|
+
defaultValue !== undefined &&
|
|
748
|
+
areSearchValuesEqual(normalized, defaultValue));
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
function appendRawSearchValue(output, key, value) {
|
|
752
|
+
const current = output[key];
|
|
753
|
+
if (current === undefined) {
|
|
754
|
+
output[key] = value;
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
output[key] = Object.freeze(Array.isArray(current) ? [...current, value] : [current, value]);
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function createSearchParams(input) {
|
|
761
|
+
if (input instanceof URLSearchParams) {
|
|
762
|
+
return new URLSearchParams(input);
|
|
763
|
+
}
|
|
764
|
+
return new URLSearchParams(input.startsWith('?') ? input.slice(1) : input);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
function parseRawSearch(input) {
|
|
768
|
+
const output = {};
|
|
769
|
+
for (const [key, value] of createSearchParams(input)) {
|
|
770
|
+
appendRawSearchValue(output, key, value);
|
|
771
|
+
}
|
|
772
|
+
return Object.freeze(output);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
function copyRawSearchParams(input) {
|
|
776
|
+
const output = {};
|
|
777
|
+
for (const [key, value] of Object.entries(input)) {
|
|
778
|
+
output[key] = Array.isArray(value) ? Object.freeze([...value]) : value;
|
|
779
|
+
}
|
|
780
|
+
return Object.freeze(output);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
function collectObjectSearchPaths(schema) {
|
|
784
|
+
return Object.freeze(collectObjectSearchPathsFromShape(schema, []));
|
|
785
|
+
}
|
|
786
|
+
function collectObjectSearchPathsFromShape(schema, basePath) {
|
|
787
|
+
const shape = getObjectSchemaShape(schema);
|
|
788
|
+
const paths = [];
|
|
789
|
+
for (const [key, childSchema] of Object.entries(shape)) {
|
|
790
|
+
const childPath = [...basePath, key];
|
|
791
|
+
if (isRuntimeSchemaKind(childSchema, 'object')) {
|
|
792
|
+
paths.push(...collectObjectSearchPathsFromShape(childSchema, childPath));
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
795
|
+
paths.push(Object.freeze(childPath));
|
|
796
|
+
}
|
|
797
|
+
return paths;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
function getObjectSearchRawKeyPath(parentKey, rawKey) {
|
|
801
|
+
const prefix = `${parentKey}.`;
|
|
802
|
+
if (!rawKey.startsWith(prefix)) {
|
|
803
|
+
return undefined;
|
|
804
|
+
}
|
|
805
|
+
const childKey = rawKey.slice(prefix.length);
|
|
806
|
+
if (!childKey) {
|
|
807
|
+
return undefined;
|
|
808
|
+
}
|
|
809
|
+
return splitObjectSearchKey(childKey);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
function createObjectSearchPathKey(path) {
|
|
813
|
+
return JSON.stringify(path);
|
|
814
|
+
}
|
|
815
|
+
function isObjectSearchPathEqual(left, right) {
|
|
816
|
+
if (left.length !== right.length) {
|
|
817
|
+
return false;
|
|
818
|
+
}
|
|
819
|
+
return left.every((value, index) => value === right[index]);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
function deleteSearchFieldRawKeys(field, rawSearch) {
|
|
823
|
+
if (isRuntimeSchemaKind(field.schema, 'object')) {
|
|
824
|
+
deleteObjectSearchKeys(field, rawSearch);
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
delete rawSearch[field.key];
|
|
828
|
+
}
|
|
829
|
+
function deleteObjectSearchKeys(field, rawSearch) {
|
|
830
|
+
const declaredPaths = collectObjectSearchPaths(field.schema);
|
|
831
|
+
for (const rawKey of Object.keys(rawSearch)) {
|
|
832
|
+
const rawPath = getObjectSearchRawKeyPath(field.key, rawKey);
|
|
833
|
+
if (rawPath &&
|
|
834
|
+
declaredPaths.some((declaredPath) => isObjectSearchPathEqual(rawPath, declaredPath))) {
|
|
835
|
+
delete rawSearch[rawKey];
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
function readArraySearchValues(value, arrayFormat = 'repeat') {
|
|
841
|
+
if (value === undefined) {
|
|
842
|
+
return undefined;
|
|
843
|
+
}
|
|
844
|
+
const values = Array.isArray(value) ? value : [value];
|
|
845
|
+
if (arrayFormat !== 'comma') {
|
|
846
|
+
return values;
|
|
847
|
+
}
|
|
848
|
+
return Object.freeze(values.flatMap((item) => item.split(',')));
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function parseArraySearchValue(schema, value, context, options = {}) {
|
|
852
|
+
if (value === undefined) {
|
|
853
|
+
return parseRuntimeSchemaValue(schema, undefined, {
|
|
854
|
+
path: context.path,
|
|
855
|
+
errorCode: context.errorCode,
|
|
856
|
+
missingCode: 'missing-search',
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
compileRuntimeSchema(schema, { path: context.path });
|
|
860
|
+
return parseArrayRuntimeSchemaValue(schema, readArraySearchValues(value, options.arrayFormat) ?? [], context);
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
function assertNoObjectSearchCollisions(parentKey, rawSearch, parentPath) {
|
|
864
|
+
const paths = new Map();
|
|
865
|
+
for (const rawKey of Object.keys(rawSearch)) {
|
|
866
|
+
const path = getObjectSearchRawKeyPath(parentKey, rawKey);
|
|
867
|
+
if (!path) {
|
|
868
|
+
continue;
|
|
869
|
+
}
|
|
870
|
+
const pathKey = createObjectSearchPathKey(path);
|
|
871
|
+
const current = paths.get(pathKey);
|
|
872
|
+
if (!current) {
|
|
873
|
+
paths.set(pathKey, { path, rawKeys: new Set([rawKey]) });
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
current.rawKeys.add(rawKey);
|
|
877
|
+
if (current.rawKeys.size > 1) {
|
|
878
|
+
throw new UrlKitError('invalid-search', 'Object search parameter keys resolve to the same object path.', {
|
|
879
|
+
path: [...parentPath, ...current.path],
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
function findObjectSearchRawValue(parentKey, path, rawSearch) {
|
|
886
|
+
for (const [rawKey, value] of Object.entries(rawSearch)) {
|
|
887
|
+
const rawPath = getObjectSearchRawKeyPath(parentKey, rawKey);
|
|
888
|
+
if (rawPath && isObjectSearchPathEqual(rawPath, path)) {
|
|
889
|
+
return value;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
return undefined;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
function parseObjectSearchValue(schema, parentKey, rawSearch, context, options = {}) {
|
|
896
|
+
assertNoObjectSearchCollisions(parentKey, rawSearch, context.path);
|
|
897
|
+
const descriptor = compileRuntimeSchema(schema, { path: context.path });
|
|
898
|
+
const hasDeclaredValues = hasDeclaredObjectValues(schema, parentKey, [], rawSearch);
|
|
899
|
+
if (!hasDeclaredValues) {
|
|
900
|
+
if (descriptor.presence === 'optional') {
|
|
901
|
+
return undefined;
|
|
902
|
+
}
|
|
903
|
+
if (descriptor.presence === 'defaulted') {
|
|
904
|
+
return descriptor.defaultValue;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
return parseObjectShape(schema, parentKey, [], rawSearch, context, options);
|
|
908
|
+
}
|
|
909
|
+
function parseObjectShape(schema, parentKey, objectPath, rawSearch, context, options) {
|
|
910
|
+
const shape = getObjectSchemaShape(schema);
|
|
911
|
+
const output = {};
|
|
912
|
+
for (const [key, childSchema] of Object.entries(shape)) {
|
|
913
|
+
const childObjectPath = [...objectPath, key];
|
|
914
|
+
const childPath = [...context.path, key];
|
|
915
|
+
const value = parseChildObjectSearchValue(childSchema, parentKey, childObjectPath, rawSearch, {
|
|
916
|
+
path: childPath,
|
|
917
|
+
errorCode: context.errorCode,
|
|
918
|
+
}, options);
|
|
919
|
+
if (value !== undefined) {
|
|
920
|
+
output[key] = value;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
return Object.freeze(output);
|
|
924
|
+
}
|
|
925
|
+
function parseChildObjectSearchValue(schema, parentKey, objectPath, rawSearch, context, options) {
|
|
926
|
+
if (isRuntimeSchemaKind(schema, 'object')) {
|
|
927
|
+
return parseNestedObjectSearchValue(schema, parentKey, objectPath, rawSearch, context, options);
|
|
928
|
+
}
|
|
929
|
+
const rawValue = findObjectSearchRawValue(parentKey, objectPath, rawSearch);
|
|
930
|
+
if (isRuntimeSchemaKind(schema, 'array')) {
|
|
931
|
+
return parseArraySearchValue(schema, rawValue, context, options);
|
|
932
|
+
}
|
|
933
|
+
if (Array.isArray(rawValue)) {
|
|
934
|
+
throw new UrlKitError('invalid-search', 'Expected a single object search parameter value.', {
|
|
935
|
+
path: context.path,
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
return parseRuntimeSchemaValue(schema, rawValue, {
|
|
939
|
+
path: context.path,
|
|
940
|
+
errorCode: context.errorCode,
|
|
941
|
+
missingCode: 'missing-search',
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
function parseNestedObjectSearchValue(schema, parentKey, objectPath, rawSearch, context, options) {
|
|
945
|
+
const descriptor = compileRuntimeSchema(schema, { path: context.path });
|
|
946
|
+
const hasDeclaredValues = hasDeclaredObjectValues(schema, parentKey, objectPath, rawSearch);
|
|
947
|
+
if (!hasDeclaredValues) {
|
|
948
|
+
if (descriptor.presence === 'optional') {
|
|
949
|
+
return undefined;
|
|
950
|
+
}
|
|
951
|
+
if (descriptor.presence === 'defaulted') {
|
|
952
|
+
return descriptor.defaultValue;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
return parseObjectShape(schema, parentKey, objectPath, rawSearch, context, options);
|
|
956
|
+
}
|
|
957
|
+
function hasDeclaredObjectValues(schema, parentKey, objectPath, rawSearch) {
|
|
958
|
+
const shape = getObjectSchemaShape(schema);
|
|
959
|
+
return Object.entries(shape).some(([key, childSchema]) => {
|
|
960
|
+
const childObjectPath = [...objectPath, key];
|
|
961
|
+
if (isRuntimeSchemaKind(childSchema, 'object')) {
|
|
962
|
+
return hasDeclaredObjectValues(childSchema, parentKey, childObjectPath, rawSearch);
|
|
963
|
+
}
|
|
964
|
+
return findObjectSearchRawValue(parentKey, childObjectPath, rawSearch) !== undefined;
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
function parseSearchFieldValue(field, rawSearch, options = {}) {
|
|
969
|
+
if (isRuntimeSchemaKind(field.schema, 'object')) {
|
|
970
|
+
return parseObjectSearchValue(field.schema, field.key, rawSearch, {
|
|
971
|
+
path: [field.key],
|
|
972
|
+
errorCode: 'invalid-search',
|
|
973
|
+
}, options);
|
|
974
|
+
}
|
|
975
|
+
if (isRuntimeSchemaKind(field.schema, 'array')) {
|
|
976
|
+
return parseArraySearchValue(field.schema, rawSearch[field.key], {
|
|
977
|
+
path: [field.key],
|
|
978
|
+
errorCode: 'invalid-search',
|
|
979
|
+
}, options);
|
|
980
|
+
}
|
|
981
|
+
return parseNonObjectSearchFieldValue(field, rawSearch[field.key], options);
|
|
982
|
+
}
|
|
983
|
+
function parseNonObjectSearchFieldValue(field, value, options) {
|
|
984
|
+
if (value === undefined) {
|
|
985
|
+
return parseMissingSearchFieldValue(field);
|
|
986
|
+
}
|
|
987
|
+
if (field.type === 'many') {
|
|
988
|
+
const values = readArraySearchValues(value, options.arrayFormat) ?? [];
|
|
989
|
+
return Object.freeze(values.map((item) => parseRuntimeSearchValue(field, item)));
|
|
990
|
+
}
|
|
991
|
+
if (Array.isArray(value)) {
|
|
992
|
+
throw new UrlKitError('invalid-search', 'Expected a single search parameter value.', {
|
|
993
|
+
path: [field.key],
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
return parseRuntimeSearchValue(field, value);
|
|
997
|
+
}
|
|
998
|
+
function parseMissingSearchFieldValue(field) {
|
|
999
|
+
if (field.presence === 'optional') {
|
|
1000
|
+
return undefined;
|
|
1001
|
+
}
|
|
1002
|
+
if (field.presence === 'defaulted') {
|
|
1003
|
+
return copyDefaultValue(field.defaultValue);
|
|
1004
|
+
}
|
|
1005
|
+
throw new UrlKitError('missing-search', 'Required search parameter is missing.', {
|
|
1006
|
+
path: [field.key],
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
function parseRuntimeSearchValue(field, value) {
|
|
1010
|
+
return parseCompiledRuntimeSchemaValue(field.compiledSchema, value, {
|
|
1011
|
+
path: [field.key],
|
|
1012
|
+
errorCode: 'invalid-search',
|
|
1013
|
+
missingCode: 'missing-search',
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
function copyDefaultValue(value) {
|
|
1017
|
+
if (Array.isArray(value)) {
|
|
1018
|
+
return Object.freeze([...value]);
|
|
1019
|
+
}
|
|
1020
|
+
if (isPlainObject(value)) {
|
|
1021
|
+
return Object.freeze({ ...value });
|
|
1022
|
+
}
|
|
1023
|
+
return value;
|
|
1024
|
+
}
|
|
1025
|
+
function isPlainObject(input) {
|
|
1026
|
+
return (typeof input === 'object' && input !== null && !Array.isArray(input) && !(input instanceof Date));
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
function parseCompiledSearch(rawSearch, compiled, unknownSearch = 'strip', options = {}) {
|
|
1030
|
+
const remainingUnknown = { ...rawSearch };
|
|
1031
|
+
const search = {};
|
|
1032
|
+
for (const field of compiled.fields) {
|
|
1033
|
+
const value = parseSearchFieldValue(field, rawSearch, options);
|
|
1034
|
+
deleteSearchFieldRawKeys(field, remainingUnknown);
|
|
1035
|
+
if (value !== undefined) {
|
|
1036
|
+
search[field.key] = value;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
const result = {
|
|
1040
|
+
search: Object.freeze(search),
|
|
1041
|
+
};
|
|
1042
|
+
const unknown = resolveUnknownSearch$1(remainingUnknown, unknownSearch);
|
|
1043
|
+
if (unknown) {
|
|
1044
|
+
return Object.freeze({
|
|
1045
|
+
...result,
|
|
1046
|
+
unknownSearch: unknown,
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
return Object.freeze(result);
|
|
1050
|
+
}
|
|
1051
|
+
function resolveUnknownSearch$1(unknown, behavior) {
|
|
1052
|
+
const keys = Object.keys(unknown);
|
|
1053
|
+
if (!keys.length || behavior === 'strip') {
|
|
1054
|
+
return undefined;
|
|
1055
|
+
}
|
|
1056
|
+
if (behavior === 'error') {
|
|
1057
|
+
throw new UrlKitError('invalid-search', 'Unknown search parameter is not allowed.', {
|
|
1058
|
+
path: [keys[0]],
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
return copyRawSearchParams(unknown);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
function copyUnknownStructuredSearch(input) {
|
|
1065
|
+
const output = {};
|
|
1066
|
+
for (const [key, value] of Object.entries(input)) {
|
|
1067
|
+
if (value === undefined || value === null) {
|
|
1068
|
+
continue;
|
|
1069
|
+
}
|
|
1070
|
+
if (typeof value === 'string') {
|
|
1071
|
+
output[key] = value;
|
|
1072
|
+
continue;
|
|
1073
|
+
}
|
|
1074
|
+
if (Array.isArray(value) && value.every((item) => typeof item === 'string')) {
|
|
1075
|
+
output[key] = Object.freeze([...value]);
|
|
1076
|
+
continue;
|
|
1077
|
+
}
|
|
1078
|
+
throw new UrlKitError('invalid-search', 'Unknown search parameter must be a string or string array to preserve.', {
|
|
1079
|
+
path: [key],
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
if (!Object.keys(output).length) {
|
|
1083
|
+
return undefined;
|
|
1084
|
+
}
|
|
1085
|
+
return Object.freeze(output);
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
function normalizeCompiledSearch(input, compiled, unknownSearch = 'strip') {
|
|
1089
|
+
if (input === undefined || input === null) {
|
|
1090
|
+
return normalizeCompiledSearchObject({}, compiled, unknownSearch);
|
|
1091
|
+
}
|
|
1092
|
+
if (!isRecord$3(input)) {
|
|
1093
|
+
throw new UrlKitError('invalid-search', 'Search must be an object.', { path: ['search'] });
|
|
1094
|
+
}
|
|
1095
|
+
return normalizeCompiledSearchObject(input, compiled, unknownSearch);
|
|
1096
|
+
}
|
|
1097
|
+
function normalizeCompiledSearchObject(input, compiled, unknownSearch) {
|
|
1098
|
+
const search = {};
|
|
1099
|
+
const knownKeys = compiled?.keys ?? emptyKeys;
|
|
1100
|
+
for (const field of compiled?.fields ?? []) {
|
|
1101
|
+
const value = normalizeSearchBuildValue(field, input[field.key]);
|
|
1102
|
+
if (value !== undefined) {
|
|
1103
|
+
search[field.key] = value;
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
const unknownValues = collectUnknownValues(input, knownKeys);
|
|
1107
|
+
const unknown = resolveUnknownSearch(unknownValues, unknownSearch);
|
|
1108
|
+
return Object.freeze({
|
|
1109
|
+
search: Object.freeze(search),
|
|
1110
|
+
...(unknown ? { unknownSearch: unknown } : {}),
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
const emptyKeys = Object.freeze(new Set());
|
|
1114
|
+
function collectUnknownValues(input, knownKeys) {
|
|
1115
|
+
const unknown = {};
|
|
1116
|
+
for (const [key, value] of Object.entries(input)) {
|
|
1117
|
+
if (!knownKeys.has(key)) {
|
|
1118
|
+
unknown[key] = value;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
return unknown;
|
|
1122
|
+
}
|
|
1123
|
+
function resolveUnknownSearch(unknown, behavior) {
|
|
1124
|
+
const keys = Object.keys(unknown);
|
|
1125
|
+
if (!keys.length || behavior === 'strip') {
|
|
1126
|
+
return undefined;
|
|
1127
|
+
}
|
|
1128
|
+
if (behavior === 'error') {
|
|
1129
|
+
throw new UrlKitError('invalid-search', 'Unknown search parameter is not allowed.', {
|
|
1130
|
+
path: [keys[0]],
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
return copyUnknownStructuredSearch(unknown);
|
|
1134
|
+
}
|
|
1135
|
+
function isRecord$3(input) {
|
|
1136
|
+
return typeof input === 'object' && input !== null && !Array.isArray(input);
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
const URL_STATE_BRAND = Symbol('urlkit.url-state');
|
|
1140
|
+
function markUrlState(state) {
|
|
1141
|
+
Object.defineProperty(state, URL_STATE_BRAND, {
|
|
1142
|
+
value: true,
|
|
1143
|
+
enumerable: false,
|
|
1144
|
+
configurable: false,
|
|
1145
|
+
writable: false,
|
|
1146
|
+
});
|
|
1147
|
+
return state;
|
|
1148
|
+
}
|
|
1149
|
+
function isUrlState(input) {
|
|
1150
|
+
return (typeof input === 'object' &&
|
|
1151
|
+
input !== null &&
|
|
1152
|
+
input[URL_STATE_BRAND] === true);
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
function normalizeCompiledUrl(input, compiled, unknownSearch) {
|
|
1156
|
+
if (!isRecord$2(input)) {
|
|
1157
|
+
throw new UrlKitError('invalid-url', 'URL state must be an object.', { path: [] });
|
|
1158
|
+
}
|
|
1159
|
+
const pathnameAndParams = normalizePathnameAndParams(input, compiled);
|
|
1160
|
+
const searchResult = normalizeCompiledSearch(input.search, compiled.search, unknownSearch);
|
|
1161
|
+
const hash = normalizeUrlHash(input.hash, compiled);
|
|
1162
|
+
const state = {
|
|
1163
|
+
pathname: pathnameAndParams.pathname,
|
|
1164
|
+
params: pathnameAndParams.params,
|
|
1165
|
+
search: searchResult.search,
|
|
1166
|
+
hash,
|
|
1167
|
+
...(searchResult.unknownSearch ? { unknownSearch: searchResult.unknownSearch } : {}),
|
|
1168
|
+
};
|
|
1169
|
+
return Object.freeze(markUrlState(state));
|
|
1170
|
+
}
|
|
1171
|
+
function normalizePathnameAndParams(input, compiled) {
|
|
1172
|
+
if (compiled.mode === 'path') {
|
|
1173
|
+
if (input.pathname !== undefined) {
|
|
1174
|
+
throw new UrlKitError('invalid-url', 'Path-based URL state must not include pathname.', {
|
|
1175
|
+
path: ['pathname'],
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1178
|
+
if (!compiled.path) {
|
|
1179
|
+
throw new UrlKitError('invalid-descriptor', 'Path URL descriptor is missing a compiled path.', { path: ['path'] });
|
|
1180
|
+
}
|
|
1181
|
+
const pathname = compiled.path.buildPath(input.params);
|
|
1182
|
+
const params = compiled.path.parsePathname(String(pathname));
|
|
1183
|
+
return Object.freeze({ pathname, params });
|
|
1184
|
+
}
|
|
1185
|
+
if (input.params !== undefined) {
|
|
1186
|
+
throw new UrlKitError('invalid-url', 'Pathless URL state must not include params.', {
|
|
1187
|
+
path: ['params'],
|
|
1188
|
+
});
|
|
1189
|
+
}
|
|
1190
|
+
return Object.freeze({
|
|
1191
|
+
pathname: normalizePathlessPathname(input.pathname),
|
|
1192
|
+
params: Object.freeze({}),
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
function normalizePathlessPathname(pathname) {
|
|
1196
|
+
if (pathname === undefined) {
|
|
1197
|
+
return '';
|
|
1198
|
+
}
|
|
1199
|
+
if (typeof pathname !== 'string') {
|
|
1200
|
+
throw new UrlKitError('invalid-url', 'Pathless pathname must be a string.', {
|
|
1201
|
+
path: ['pathname'],
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
return pathname;
|
|
1205
|
+
}
|
|
1206
|
+
function normalizeUrlHash(input, compiled) {
|
|
1207
|
+
if (compiled.hash) {
|
|
1208
|
+
return compiled.hash.normalize(input);
|
|
1209
|
+
}
|
|
1210
|
+
if (input !== undefined && input !== null) {
|
|
1211
|
+
throw new UrlKitError('invalid-hash', 'Hash is not declared for this URL contract.', {
|
|
1212
|
+
path: ['hash'],
|
|
1213
|
+
});
|
|
1214
|
+
}
|
|
1215
|
+
return undefined;
|
|
1216
|
+
}
|
|
1217
|
+
function isRecord$2(input) {
|
|
1218
|
+
return typeof input === 'object' && input !== null && !Array.isArray(input);
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
function buildCompiledUrl(input, compiled, options = {}) {
|
|
1222
|
+
if (!isRecord$1(input)) {
|
|
1223
|
+
throw new UrlKitError('invalid-url', 'URL build input must be an object.', { path: [] });
|
|
1224
|
+
}
|
|
1225
|
+
const pathname = buildUrlPathname(input, compiled);
|
|
1226
|
+
const search = buildUrlSearch(input.search, compiled, options);
|
|
1227
|
+
const hash = buildUrlHash(input.hash, compiled, options);
|
|
1228
|
+
return `${pathname}${search}${hash}`;
|
|
1229
|
+
}
|
|
1230
|
+
function buildUrlPathname(input, compiled) {
|
|
1231
|
+
if (compiled.mode === 'path') {
|
|
1232
|
+
if (input.pathname !== undefined && !isUrlState(input)) {
|
|
1233
|
+
throw new UrlKitError('invalid-url', 'Path-based URL build input must not include pathname.', { path: ['pathname'] });
|
|
1234
|
+
}
|
|
1235
|
+
if (!compiled.path) {
|
|
1236
|
+
throw new UrlKitError('invalid-descriptor', 'Path URL descriptor is missing a compiled path.', { path: ['path'] });
|
|
1237
|
+
}
|
|
1238
|
+
return compiled.path.buildPath(input.params);
|
|
1239
|
+
}
|
|
1240
|
+
if (input.params !== undefined && !isUrlState(input)) {
|
|
1241
|
+
throw new UrlKitError('invalid-url', 'Pathless URL build input must not include params.', {
|
|
1242
|
+
path: ['params'],
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
if (input.pathname === undefined) {
|
|
1246
|
+
return '';
|
|
1247
|
+
}
|
|
1248
|
+
if (typeof input.pathname !== 'string') {
|
|
1249
|
+
throw new UrlKitError('invalid-url', 'Pathless pathname must be a string.', {
|
|
1250
|
+
path: ['pathname'],
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
return input.pathname;
|
|
1254
|
+
}
|
|
1255
|
+
function buildUrlSearch(input, compiled, options) {
|
|
1256
|
+
if (input === undefined) {
|
|
1257
|
+
return compiled.search ? buildCompiledSearch({}, compiled.search, options) : '';
|
|
1258
|
+
}
|
|
1259
|
+
if (!isRecord$1(input)) {
|
|
1260
|
+
throw new UrlKitError('invalid-search', 'URL search input must be an object.', {
|
|
1261
|
+
path: ['search'],
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
if (!compiled.search) {
|
|
1265
|
+
return '';
|
|
1266
|
+
}
|
|
1267
|
+
return buildCompiledSearch(input, compiled.search, options);
|
|
1268
|
+
}
|
|
1269
|
+
function buildUrlHash(input, compiled, options) {
|
|
1270
|
+
if (compiled.hash) {
|
|
1271
|
+
return buildHash(input, compiled.hash.descriptor, options);
|
|
1272
|
+
}
|
|
1273
|
+
if (input !== undefined && input !== null) {
|
|
1274
|
+
throw new UrlKitError('invalid-hash', 'Hash is not declared for this URL contract.', {
|
|
1275
|
+
path: ['hash'],
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
return '';
|
|
1279
|
+
}
|
|
1280
|
+
function isRecord$1(input) {
|
|
1281
|
+
return typeof input === 'object' && input !== null && !Array.isArray(input);
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
const URLKIT_BASE_URL = 'http://urlkit.local';
|
|
1285
|
+
function parseUrl(input) {
|
|
1286
|
+
if (input instanceof URL) {
|
|
1287
|
+
return freezeParsedUrlInput(input);
|
|
1288
|
+
}
|
|
1289
|
+
if (typeof input !== 'string') {
|
|
1290
|
+
throw new UrlKitError('invalid-url', 'URL input must be a string or URL.', { path: [] });
|
|
1291
|
+
}
|
|
1292
|
+
try {
|
|
1293
|
+
return freezeParsedUrlInput(new URL(input, URLKIT_BASE_URL));
|
|
1294
|
+
}
|
|
1295
|
+
catch (error) {
|
|
1296
|
+
throw new UrlKitError('invalid-url', 'URL input is not valid.', { path: [], cause: error });
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
function freezeParsedUrlInput(input) {
|
|
1300
|
+
return Object.freeze({
|
|
1301
|
+
pathname: input.pathname,
|
|
1302
|
+
searchParams: new URLSearchParams(input.searchParams),
|
|
1303
|
+
hash: input.hash,
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
function resolveUrlUnknownSearch(rawSearch, behavior) {
|
|
1308
|
+
const keys = Object.keys(rawSearch);
|
|
1309
|
+
if (!keys.length || behavior === 'strip') {
|
|
1310
|
+
return undefined;
|
|
1311
|
+
}
|
|
1312
|
+
if (behavior === 'error') {
|
|
1313
|
+
throw new UrlKitError('invalid-search', 'Unknown search parameter is not allowed.', {
|
|
1314
|
+
path: [keys[0]],
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
return copyRawSearchParams(rawSearch);
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
function parseCompiledUrl(input, compiled, unknownSearch, options = {}) {
|
|
1321
|
+
const parsedUrl = parseUrl(input);
|
|
1322
|
+
const params = parseUrlParams(parsedUrl.pathname, compiled);
|
|
1323
|
+
const searchResult = parseUrlSearch(parsedUrl.searchParams, compiled, unknownSearch, options);
|
|
1324
|
+
const hash = parseUrlHash(parsedUrl.hash, compiled);
|
|
1325
|
+
const state = {
|
|
1326
|
+
pathname: parsedUrl.pathname,
|
|
1327
|
+
params,
|
|
1328
|
+
search: searchResult.search,
|
|
1329
|
+
hash,
|
|
1330
|
+
...(searchResult.unknownSearch ? { unknownSearch: searchResult.unknownSearch } : {}),
|
|
1331
|
+
};
|
|
1332
|
+
return Object.freeze(markUrlState(state));
|
|
1333
|
+
}
|
|
1334
|
+
function parseUrlParams(pathname, compiled) {
|
|
1335
|
+
if (compiled.mode === 'path' && compiled.path) {
|
|
1336
|
+
return compiled.path.parsePathname(pathname);
|
|
1337
|
+
}
|
|
1338
|
+
return Object.freeze({});
|
|
1339
|
+
}
|
|
1340
|
+
function parseUrlSearch(searchParams, compiled, unknownSearch, options) {
|
|
1341
|
+
const rawSearch = parseRawSearch(searchParams);
|
|
1342
|
+
if (compiled.search) {
|
|
1343
|
+
return parseCompiledSearch(rawSearch, compiled.search, unknownSearch, options);
|
|
1344
|
+
}
|
|
1345
|
+
const unknown = resolveUrlUnknownSearch(rawSearch, unknownSearch);
|
|
1346
|
+
return Object.freeze({
|
|
1347
|
+
search: Object.freeze({}),
|
|
1348
|
+
...(unknown ? { unknownSearch: unknown } : {}),
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
function parseUrlHash(hash, compiled) {
|
|
1352
|
+
if (!compiled.hash) {
|
|
1353
|
+
return undefined;
|
|
1354
|
+
}
|
|
1355
|
+
return compiled.hash.parse(readHashFragment(hash));
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
function resolveRequestUrlInput(input, options = {}) {
|
|
1359
|
+
const url = readRequestUrl(input);
|
|
1360
|
+
if (options.baseUrl) {
|
|
1361
|
+
return resolveRequestUrl(url, options.baseUrl);
|
|
1362
|
+
}
|
|
1363
|
+
return url;
|
|
1364
|
+
}
|
|
1365
|
+
function readRequestUrl(input) {
|
|
1366
|
+
if (isRequest(input)) {
|
|
1367
|
+
return input.url;
|
|
1368
|
+
}
|
|
1369
|
+
if (isUrlRequestInput(input)) {
|
|
1370
|
+
return input.url;
|
|
1371
|
+
}
|
|
1372
|
+
throw new UrlKitError('invalid-url', 'Request input must be a Request or an object with a url string.', {
|
|
1373
|
+
path: [],
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1376
|
+
function resolveRequestUrl(url, baseUrl) {
|
|
1377
|
+
try {
|
|
1378
|
+
return new URL(url, baseUrl);
|
|
1379
|
+
}
|
|
1380
|
+
catch (error) {
|
|
1381
|
+
throw new UrlKitError('invalid-url', 'Request URL is not valid.', {
|
|
1382
|
+
path: [],
|
|
1383
|
+
cause: error,
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
function isRequest(input) {
|
|
1388
|
+
return typeof Request !== 'undefined' && input instanceof Request;
|
|
1389
|
+
}
|
|
1390
|
+
function isUrlRequestInput(input) {
|
|
1391
|
+
return (typeof input === 'object' &&
|
|
1392
|
+
input !== null &&
|
|
1393
|
+
'url' in input &&
|
|
1394
|
+
typeof input.url === 'string');
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
function matchCompiledUrl(input, compiled, unknownSearch, options = {}) {
|
|
1398
|
+
try {
|
|
1399
|
+
parseCompiledUrl(input, compiled, unknownSearch, options);
|
|
1400
|
+
return true;
|
|
1401
|
+
}
|
|
1402
|
+
catch (error) {
|
|
1403
|
+
if (error instanceof UrlKitError) {
|
|
1404
|
+
return false;
|
|
1405
|
+
}
|
|
1406
|
+
return false;
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
function buildRawSearch(input = {}, options = {}) {
|
|
1411
|
+
const entries = [];
|
|
1412
|
+
for (const [key, value] of Object.entries(input)) {
|
|
1413
|
+
appendRawValue(entries, key, value, options);
|
|
1414
|
+
}
|
|
1415
|
+
return serializeSearchEntries(entries, options);
|
|
1416
|
+
}
|
|
1417
|
+
function appendRawValue(entries, key, value, options) {
|
|
1418
|
+
if (value === undefined || value === null) {
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1421
|
+
if (Array.isArray(value)) {
|
|
1422
|
+
appendSearchEntry(entries, key, value.filter(isPresent$1).map(String), options);
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
appendSearchEntry(entries, key, serializeRawScalar(value), options);
|
|
1426
|
+
}
|
|
1427
|
+
function isPresent$1(input) {
|
|
1428
|
+
return input !== undefined && input !== null;
|
|
1429
|
+
}
|
|
1430
|
+
function serializeRawScalar(input) {
|
|
1431
|
+
if (typeof input === 'string') {
|
|
1432
|
+
return input;
|
|
1433
|
+
}
|
|
1434
|
+
if (typeof input === 'number' || typeof input === 'boolean' || typeof input === 'bigint') {
|
|
1435
|
+
return String(input);
|
|
1436
|
+
}
|
|
1437
|
+
return JSON.stringify(input) ?? String(input);
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
function omitRawSearch(input, keys) {
|
|
1441
|
+
const keySet = new Set(keys);
|
|
1442
|
+
const output = {};
|
|
1443
|
+
for (const [key, value] of Object.entries(input)) {
|
|
1444
|
+
if (keySet.has(key)) {
|
|
1445
|
+
continue;
|
|
1446
|
+
}
|
|
1447
|
+
output[key] = copyRawSearchValue(value);
|
|
1448
|
+
}
|
|
1449
|
+
return Object.freeze(output);
|
|
1450
|
+
}
|
|
1451
|
+
function pickRawSearch(input, keys) {
|
|
1452
|
+
const keySet = new Set(keys);
|
|
1453
|
+
const output = {};
|
|
1454
|
+
for (const [key, value] of Object.entries(input)) {
|
|
1455
|
+
if (!keySet.has(key)) {
|
|
1456
|
+
continue;
|
|
1457
|
+
}
|
|
1458
|
+
output[key] = copyRawSearchValue(value);
|
|
1459
|
+
}
|
|
1460
|
+
return Object.freeze(output);
|
|
1461
|
+
}
|
|
1462
|
+
function copyRawSearchValue(value) {
|
|
1463
|
+
return Array.isArray(value) ? Object.freeze([...value]) : value;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
function formatParsedUrl(input, search) {
|
|
1467
|
+
return `${input.pathname}${search}${input.hash}`;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
function omitCompiledUrlSearch(input, keys, _compiled, options = {}) {
|
|
1471
|
+
const parsed = parseUrl(input);
|
|
1472
|
+
const filtered = omitRawSearch(parseRawSearch(parsed.searchParams), keys);
|
|
1473
|
+
return formatParsedUrl(parsed, buildRawSearch(filtered, options));
|
|
1474
|
+
}
|
|
1475
|
+
function pickCompiledUrlSearch(input, keys, _compiled, options = {}) {
|
|
1476
|
+
const parsed = parseUrl(input);
|
|
1477
|
+
const filtered = pickRawSearch(parseRawSearch(parsed.searchParams), keys);
|
|
1478
|
+
return formatParsedUrl(parsed, buildRawSearch(filtered, options));
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
function joinSearchStrings(...parts) {
|
|
1482
|
+
const body = parts
|
|
1483
|
+
.map((part) => part.replace(/^\?/, ''))
|
|
1484
|
+
.filter(Boolean)
|
|
1485
|
+
.join('&');
|
|
1486
|
+
return body ? `?${body}` : '';
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
function hasSearchFieldRawValue(field, rawSearch) {
|
|
1490
|
+
if (isRuntimeSchemaKind(field.schema, 'object')) {
|
|
1491
|
+
return hasObjectRawValue(field.schema, field.key, rawSearch);
|
|
1492
|
+
}
|
|
1493
|
+
return rawSearch[field.key] !== undefined;
|
|
1494
|
+
}
|
|
1495
|
+
function hasObjectRawValue(schema, parentKey, rawSearch) {
|
|
1496
|
+
const shape = getObjectSchemaShape(schema);
|
|
1497
|
+
return Object.entries(shape).some(([key, childSchema]) => {
|
|
1498
|
+
const childSearchKey = joinObjectSearchKey(parentKey, key);
|
|
1499
|
+
if (isRuntimeSchemaKind(childSchema, 'object')) {
|
|
1500
|
+
return hasObjectRawValue(childSchema, childSearchKey, rawSearch);
|
|
1501
|
+
}
|
|
1502
|
+
return rawSearch[childSearchKey] !== undefined;
|
|
1503
|
+
});
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
function parsePartialCompiledSearch(rawSearch, compiled, options = {}) {
|
|
1507
|
+
const remainingUnknown = { ...rawSearch };
|
|
1508
|
+
const search = {};
|
|
1509
|
+
for (const field of compiled.fields) {
|
|
1510
|
+
if (!hasSearchFieldRawValue(field, rawSearch)) {
|
|
1511
|
+
continue;
|
|
1512
|
+
}
|
|
1513
|
+
const value = parseSearchFieldValue(field, rawSearch, options);
|
|
1514
|
+
deleteSearchFieldRawKeys(field, remainingUnknown);
|
|
1515
|
+
if (value !== undefined) {
|
|
1516
|
+
search[field.key] = value;
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
return Object.freeze({
|
|
1520
|
+
search: Object.freeze(search),
|
|
1521
|
+
unknownSearch: copyRawSearchParams(remainingUnknown),
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
function patchCompiledUrlSearch(input, patch, compiled, options = {}) {
|
|
1526
|
+
const parsed = parseUrl(input);
|
|
1527
|
+
const rawSearch = parseRawSearch(parsed.searchParams);
|
|
1528
|
+
if (!compiled.search) {
|
|
1529
|
+
return formatParsedUrl(parsed, buildRawPatchSearch(rawSearch, patch, options));
|
|
1530
|
+
}
|
|
1531
|
+
const currentParsed = parsePartialCompiledSearch(rawSearch, compiled.search, options);
|
|
1532
|
+
const mergedSearch = { ...currentParsed.search };
|
|
1533
|
+
const unknownSearch = {
|
|
1534
|
+
...copyRawSearchParams(currentParsed.unknownSearch),
|
|
1535
|
+
};
|
|
1536
|
+
const schemaKeys = compiled.search.keys;
|
|
1537
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
1538
|
+
if (shouldRemoveUndefined(value, options) || shouldRemoveNull(value, options)) {
|
|
1539
|
+
delete mergedSearch[key];
|
|
1540
|
+
delete unknownSearch[key];
|
|
1541
|
+
continue;
|
|
1542
|
+
}
|
|
1543
|
+
if (value === undefined || value === null) {
|
|
1544
|
+
continue;
|
|
1545
|
+
}
|
|
1546
|
+
if (schemaKeys.has(key)) {
|
|
1547
|
+
mergedSearch[key] = value;
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
const search = joinSearchStrings(buildCompiledSearch(mergedSearch, compiled.search, options), buildRawSearch(unknownSearch, options));
|
|
1551
|
+
return formatParsedUrl(parsed, search);
|
|
1552
|
+
}
|
|
1553
|
+
function buildRawPatchSearch(rawSearch, patch, options) {
|
|
1554
|
+
const merged = { ...rawSearch };
|
|
1555
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
1556
|
+
if (shouldRemoveUndefined(value, options) || shouldRemoveNull(value, options)) {
|
|
1557
|
+
delete merged[key];
|
|
1558
|
+
continue;
|
|
1559
|
+
}
|
|
1560
|
+
if (value === undefined || value === null) {
|
|
1561
|
+
continue;
|
|
1562
|
+
}
|
|
1563
|
+
merged[key] = toRawSearchValue(value);
|
|
1564
|
+
}
|
|
1565
|
+
return buildRawSearch(merged, options);
|
|
1566
|
+
}
|
|
1567
|
+
function shouldRemoveUndefined(value, options) {
|
|
1568
|
+
return value === undefined && options.removeUndefined === true;
|
|
1569
|
+
}
|
|
1570
|
+
function shouldRemoveNull(value, options) {
|
|
1571
|
+
return value === null && options.removeNull === true;
|
|
1572
|
+
}
|
|
1573
|
+
function toRawSearchValue(value) {
|
|
1574
|
+
if (Array.isArray(value)) {
|
|
1575
|
+
return Object.freeze(value.filter(isPresent).map(String));
|
|
1576
|
+
}
|
|
1577
|
+
return String(value);
|
|
1578
|
+
}
|
|
1579
|
+
function isPresent(value) {
|
|
1580
|
+
return value !== undefined && value !== null;
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
function replaceCompiledUrlSearch(input, next, compiled, options = {}) {
|
|
1584
|
+
const parsed = parseUrl(input);
|
|
1585
|
+
const search = compiled.search
|
|
1586
|
+
? buildCompiledSearch(next, compiled.search, options)
|
|
1587
|
+
: buildRawSearch(next, options);
|
|
1588
|
+
return formatParsedUrl(parsed, search);
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
function compileUrlDescriptor(descriptor) {
|
|
1592
|
+
assertNormalizedUrlDescriptor(descriptor);
|
|
1593
|
+
const path = compileUrlPath(descriptor);
|
|
1594
|
+
const search = descriptor.search ? compileSearchSchema(descriptor.search) : undefined;
|
|
1595
|
+
const hash = descriptor.hash ? compileHashDescriptor(descriptor.hash) : undefined;
|
|
1596
|
+
return Object.freeze({
|
|
1597
|
+
mode: descriptor.mode,
|
|
1598
|
+
pattern: descriptor.pattern,
|
|
1599
|
+
path,
|
|
1600
|
+
...(descriptor.search ? { searchSchema: descriptor.search, search } : {}),
|
|
1601
|
+
...(hash ? { hash } : {}),
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
function compileUrlPath(descriptor) {
|
|
1605
|
+
if (descriptor.mode === 'path') {
|
|
1606
|
+
if (descriptor.path) {
|
|
1607
|
+
return descriptor.path;
|
|
1608
|
+
}
|
|
1609
|
+
if (typeof descriptor.pattern === 'string') {
|
|
1610
|
+
return compilePath(descriptor.pattern);
|
|
1611
|
+
}
|
|
1612
|
+
throw new UrlKitError('invalid-descriptor', 'Path URL descriptor must include a path pattern.', { path: ['path'] });
|
|
1613
|
+
}
|
|
1614
|
+
return undefined;
|
|
1615
|
+
}
|
|
1616
|
+
function assertNormalizedUrlDescriptor(input) {
|
|
1617
|
+
if (!isRecord(input)) {
|
|
1618
|
+
throw new UrlKitError('invalid-descriptor', 'URL descriptor must be an object.', { path: [] });
|
|
1619
|
+
}
|
|
1620
|
+
if (input.mode !== 'path' && input.mode !== 'pathless') {
|
|
1621
|
+
throw new UrlKitError('invalid-descriptor', 'URL descriptor mode must be path or pathless.', {
|
|
1622
|
+
path: ['mode'],
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
if (input.mode === 'path' && typeof input.pattern !== 'string') {
|
|
1626
|
+
throw new UrlKitError('invalid-descriptor', 'Path URL descriptor pattern must be a string.', {
|
|
1627
|
+
path: ['pattern'],
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
if (input.mode === 'pathless' && input.pattern !== undefined) {
|
|
1631
|
+
throw new UrlKitError('invalid-descriptor', 'Pathless URL descriptor pattern must be undefined.', { path: ['pattern'] });
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
function isRecord(input) {
|
|
1635
|
+
return typeof input === 'object' && input !== null && !Array.isArray(input);
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
function createUrlContract(descriptor, options = {}) {
|
|
1639
|
+
const compiled = compileUrlDescriptor(descriptor);
|
|
1640
|
+
const unknownSearch = options.unknownSearch ?? 'strip';
|
|
1641
|
+
const arrayFormat = options.arrayFormat ?? 'repeat';
|
|
1642
|
+
const contract = {
|
|
1643
|
+
pattern: compiled.pattern,
|
|
1644
|
+
parse(input, options) {
|
|
1645
|
+
return parseCompiledUrl(input, compiled, options?.unknownSearch ?? unknownSearch, { arrayFormat: options?.arrayFormat ?? arrayFormat });
|
|
1646
|
+
},
|
|
1647
|
+
safeParse(input, options) {
|
|
1648
|
+
try {
|
|
1649
|
+
return Object.freeze({
|
|
1650
|
+
success: true,
|
|
1651
|
+
data: parseCompiledUrl(input, compiled, options?.unknownSearch ?? unknownSearch, { arrayFormat: options?.arrayFormat ?? arrayFormat }),
|
|
1652
|
+
});
|
|
1653
|
+
}
|
|
1654
|
+
catch (error) {
|
|
1655
|
+
return Object.freeze({
|
|
1656
|
+
success: false,
|
|
1657
|
+
error: error instanceof UrlKitError ? error : new UrlKitError('invalid-url', { cause: error }),
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
},
|
|
1661
|
+
parseRequest(input, options) {
|
|
1662
|
+
return parseCompiledUrl(resolveRequestUrlInput(input, options), compiled, options?.unknownSearch ?? unknownSearch, { arrayFormat: options?.arrayFormat ?? arrayFormat });
|
|
1663
|
+
},
|
|
1664
|
+
safeParseRequest(input, options) {
|
|
1665
|
+
try {
|
|
1666
|
+
return Object.freeze({
|
|
1667
|
+
success: true,
|
|
1668
|
+
data: parseCompiledUrl(resolveRequestUrlInput(input, options), compiled, options?.unknownSearch ?? unknownSearch, { arrayFormat: options?.arrayFormat ?? arrayFormat }),
|
|
1669
|
+
});
|
|
1670
|
+
}
|
|
1671
|
+
catch (error) {
|
|
1672
|
+
return Object.freeze({
|
|
1673
|
+
success: false,
|
|
1674
|
+
error: error instanceof UrlKitError ? error : new UrlKitError('invalid-url', { cause: error }),
|
|
1675
|
+
});
|
|
1676
|
+
}
|
|
1677
|
+
},
|
|
1678
|
+
normalize(input, options) {
|
|
1679
|
+
return normalizeCompiledUrl(input, compiled, options?.unknownSearch ?? unknownSearch);
|
|
1680
|
+
},
|
|
1681
|
+
safeNormalize(input, options) {
|
|
1682
|
+
try {
|
|
1683
|
+
return Object.freeze({
|
|
1684
|
+
success: true,
|
|
1685
|
+
data: normalizeCompiledUrl(input, compiled, options?.unknownSearch ?? unknownSearch),
|
|
1686
|
+
});
|
|
1687
|
+
}
|
|
1688
|
+
catch (error) {
|
|
1689
|
+
return Object.freeze({
|
|
1690
|
+
success: false,
|
|
1691
|
+
error: error instanceof UrlKitError ? error : new UrlKitError('invalid-url', { cause: error }),
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
},
|
|
1695
|
+
build(input, options) {
|
|
1696
|
+
return buildCompiledUrl(input, compiled, mergeBuildOptions(arrayFormat, options));
|
|
1697
|
+
},
|
|
1698
|
+
match(input, options) {
|
|
1699
|
+
return matchCompiledUrl(input, compiled, options?.unknownSearch ?? unknownSearch, {
|
|
1700
|
+
arrayFormat: options?.arrayFormat ?? arrayFormat,
|
|
1701
|
+
});
|
|
1702
|
+
},
|
|
1703
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
1704
|
+
parsePathname: compiled.path?.parsePathname,
|
|
1705
|
+
buildPath: compiled.path?.buildPath,
|
|
1706
|
+
parseSearch(input, options) {
|
|
1707
|
+
if (compiled.search) {
|
|
1708
|
+
return parseCompiledSearch(parseRawSearch(input), compiled.search, options?.unknownSearch ?? unknownSearch, { arrayFormat: options?.arrayFormat ?? arrayFormat }).search;
|
|
1709
|
+
}
|
|
1710
|
+
return Object.freeze({});
|
|
1711
|
+
},
|
|
1712
|
+
parseHash(input) {
|
|
1713
|
+
return compiled.hash
|
|
1714
|
+
? parseHash(input, compiled.hash.descriptor)
|
|
1715
|
+
: parseHash(input);
|
|
1716
|
+
},
|
|
1717
|
+
buildSearch(search, options) {
|
|
1718
|
+
if (compiled.search) {
|
|
1719
|
+
return buildCompiledSearch(search, compiled.search, mergeBuildOptions(arrayFormat, options));
|
|
1720
|
+
}
|
|
1721
|
+
return '';
|
|
1722
|
+
},
|
|
1723
|
+
buildHash(hash, options) {
|
|
1724
|
+
return compiled.hash
|
|
1725
|
+
? buildHash(hash, compiled.hash.descriptor, options)
|
|
1726
|
+
: buildHash(hash, options);
|
|
1727
|
+
},
|
|
1728
|
+
withSearch(input, search, options) {
|
|
1729
|
+
return patchCompiledUrlSearch(input, search, compiled, mergeBuildOptions(arrayFormat, options));
|
|
1730
|
+
},
|
|
1731
|
+
replaceSearch(input, search, options) {
|
|
1732
|
+
return replaceCompiledUrlSearch(input, search, compiled, mergeBuildOptions(arrayFormat, options));
|
|
1733
|
+
},
|
|
1734
|
+
omitSearch(input, keys, options) {
|
|
1735
|
+
return omitCompiledUrlSearch(input, keys, compiled, mergeBuildOptions(arrayFormat, options));
|
|
1736
|
+
},
|
|
1737
|
+
pickSearch(input, keys, options) {
|
|
1738
|
+
return pickCompiledUrlSearch(input, keys, compiled, mergeBuildOptions(arrayFormat, options));
|
|
1739
|
+
},
|
|
1740
|
+
};
|
|
1741
|
+
return Object.freeze(contract);
|
|
1742
|
+
}
|
|
1743
|
+
function mergeBuildOptions(arrayFormat, options) {
|
|
1744
|
+
return {
|
|
1745
|
+
...options,
|
|
1746
|
+
arrayFormat: options?.arrayFormat ?? arrayFormat,
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
export { createUrlContract as a, array as b, compileHashDescriptor as c, buildCompiledSearch as d, buildRawSearch as e, omitRawSearch as f, getObjectSchemaShape as g, parseRawSearch as h, hasSearchFieldRawValue as i, parseSearchFieldValue as j, deleteSearchFieldRawKeys as k, copyRawSearchParams as l, joinSearchStrings as m, pickRawSearch as n, object as o, parseArrayRuntimeSchemaValue as p, parseCompiledSearch as q, readHashFragment as r, serializeArrayRuntimeSchemaValue as s, buildHash as t, parseHash as u };
|
|
1751
|
+
//# sourceMappingURL=create-url-contract-BYKPM9bn.js.map
|