@securitydept/client 0.2.0 → 0.3.0-beta.10
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/README.md +6 -0
- package/dist/create-i5VyYgN6.mjs +989 -0
- package/dist/create-i5VyYgN6.mjs.map +1 -0
- package/dist/handle-DwMgqe7R.mjs +638 -0
- package/dist/handle-DwMgqe7R.mjs.map +1 -0
- package/dist/index-Bo1BtjZH.d.mts +132 -0
- package/dist/index-CFAJ969E.d.mts +674 -0
- package/dist/index-CbeZ3AfB.d.mts +1680 -0
- package/dist/index.d.mts +3 -353
- package/dist/index.mjs +1196 -401
- package/dist/index.mjs.map +1 -1
- package/dist/router-at5bTdJQ.mjs +245 -0
- package/dist/router-at5bTdJQ.mjs.map +1 -0
- package/dist/rx/index.d.mts +68 -0
- package/dist/rx/index.mjs +2 -0
- package/dist/rx-DM6Ouv-m.mjs +1119 -0
- package/dist/rx-DM6Ouv-m.mjs.map +1 -0
- package/dist/server/index.d.mts +20 -0
- package/dist/server/index.mjs +29 -0
- package/dist/server/index.mjs.map +1 -0
- package/dist/test/index.d.mts +101 -0
- package/dist/test/index.mjs +270 -0
- package/dist/test/index.mjs.map +1 -0
- package/dist/uri-string-BPNbIMgv.mjs +300 -0
- package/dist/uri-string-BPNbIMgv.mjs.map +1 -0
- package/dist/web/index.d.mts +82 -337
- package/dist/web/index.mjs +334 -337
- package/dist/web/index.mjs.map +1 -1
- package/dist/webext/index.d.mts +73 -0
- package/dist/webext/index.mjs +231 -0
- package/dist/webext/index.mjs.map +1 -0
- package/package.json +28 -21
- package/dist/auth-coordination/index.d.mts +0 -198
- package/dist/auth-coordination/index.mjs +0 -107
- package/dist/auth-coordination/index.mjs.map +0 -1
- package/dist/events/index.d.mts +0 -2
- package/dist/events/index.mjs +0 -2
- package/dist/events-BUlfsRfl.mjs +0 -127
- package/dist/events-BUlfsRfl.mjs.map +0 -1
- package/dist/fetch-transport-Bd5RyWj2.d.mts +0 -339
- package/dist/fetch-transport-DtnSRWAH.mjs +0 -631
- package/dist/fetch-transport-DtnSRWAH.mjs.map +0 -1
- package/dist/index-C_wgl6dW.d.mts +0 -102
- package/dist/index-DbYSFHDe.d.mts +0 -39
- package/dist/persistence/index.d.mts +0 -3
- package/dist/persistence/index.mjs +0 -2
- package/dist/persistence/web/index.d.mts +0 -16
- package/dist/persistence/web/index.mjs +0 -51
- package/dist/persistence/web/index.mjs.map +0 -1
- package/dist/persistence-CSB8770k.mjs +0 -108
- package/dist/persistence-CSB8770k.mjs.map +0 -1
- package/dist/planner-host-CPyGNyQV.d.mts +0 -190
- package/dist/planner-host-CV38bMSn.mjs +0 -195
- package/dist/planner-host-CV38bMSn.mjs.map +0 -1
- package/dist/types-C59fcM5W.d.mts +0 -52
- package/dist/types-DQc088ZP.d.mts +0 -32
- package/dist/web-router/index.d.mts +0 -259
- package/dist/web-router/index.mjs +0 -395
- package/dist/web-router/index.mjs.map +0 -1
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { H as ClientError, U as ClientErrorKind, j as SYMBOL_OBSERVABLE } from "../rx-DM6Ouv-m.mjs";
|
|
2
|
+
import { $ as notMissingProvider, I as PopupAttachFailureReason, O as RouterNavigationMode, Q as getSecuritydeptProviderToken, R as throwValidationClientError, T as TIME_TRAIT_TOKEN, Z as createProviderIfTokenMissing, c as createInMemoryRecordStore, h as TRANSPORT_TRAIT_TOKEN, t as createFoundationEnvironment, z as validateTraitInput } from "../create-i5VyYgN6.mjs";
|
|
3
|
+
import { n as UriReferenceString } from "../uri-string-BPNbIMgv.mjs";
|
|
4
|
+
import { type } from "arktype";
|
|
5
|
+
//#region src/test/time.ts
|
|
6
|
+
const TimeForTestCreateOptionsSchema = type({ initialNow: "number" });
|
|
7
|
+
function createTimeForTest(options = {}) {
|
|
8
|
+
const { validators, ...createOptions } = options;
|
|
9
|
+
const resolvedCreateOptions = { initialNow: createOptions.initialNow ?? 0 };
|
|
10
|
+
validateTraitInput({
|
|
11
|
+
value: resolvedCreateOptions,
|
|
12
|
+
bundledSchema: TimeForTestCreateOptionsSchema,
|
|
13
|
+
validator: validators?.time,
|
|
14
|
+
onInvalid: (failure) => throwValidationClientError({
|
|
15
|
+
code: "test.time.invalid_options",
|
|
16
|
+
source: "test",
|
|
17
|
+
messagePrefix: "createTimeForTest could not validate time",
|
|
18
|
+
failure
|
|
19
|
+
})
|
|
20
|
+
});
|
|
21
|
+
let now = resolvedCreateOptions.initialNow;
|
|
22
|
+
let nextId = 1;
|
|
23
|
+
const tasks = /* @__PURE__ */ new Map();
|
|
24
|
+
return {
|
|
25
|
+
now: () => now,
|
|
26
|
+
setTimeout(handler, delayMs) {
|
|
27
|
+
const id = nextId++;
|
|
28
|
+
tasks.set(id, {
|
|
29
|
+
at: now + Math.max(0, delayMs),
|
|
30
|
+
handler
|
|
31
|
+
});
|
|
32
|
+
return id;
|
|
33
|
+
},
|
|
34
|
+
clearTimeout(handle) {
|
|
35
|
+
if (typeof handle === "number") tasks.delete(handle);
|
|
36
|
+
},
|
|
37
|
+
advance(ms) {
|
|
38
|
+
now += ms;
|
|
39
|
+
},
|
|
40
|
+
flush() {
|
|
41
|
+
let progressed = true;
|
|
42
|
+
while (progressed) {
|
|
43
|
+
progressed = false;
|
|
44
|
+
const ready = [...tasks.entries()].filter(([, task]) => task.at <= now).sort((a, b) => a[1].at - b[1].at);
|
|
45
|
+
for (const [id, task] of ready) {
|
|
46
|
+
tasks.delete(id);
|
|
47
|
+
task.handler();
|
|
48
|
+
progressed = true;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
advanceAndFlush(ms) {
|
|
53
|
+
this.advance(ms);
|
|
54
|
+
this.flush();
|
|
55
|
+
},
|
|
56
|
+
get pendingCount() {
|
|
57
|
+
return tasks.size;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/test/environment.ts
|
|
63
|
+
function createEnvironmentForTest(options = {}) {
|
|
64
|
+
const { createBaseEnvironment = createFoundationEnvironment, transport, providers = [], ...baseOptions } = options;
|
|
65
|
+
const externalProviderTokens = new Set(providers.map((provider) => getSecuritydeptProviderToken(provider)));
|
|
66
|
+
const testProviders = [createProviderIfTokenMissing(externalProviderTokens, TRANSPORT_TRAIT_TOKEN, () => ({
|
|
67
|
+
provide: TRANSPORT_TRAIT_TOKEN,
|
|
68
|
+
useValue: transport ?? createMissingTransportForTest()
|
|
69
|
+
})), createProviderIfTokenMissing(externalProviderTokens, TIME_TRAIT_TOKEN, () => ({
|
|
70
|
+
provide: TIME_TRAIT_TOKEN,
|
|
71
|
+
useValue: baseOptions.time ?? createTimeForTest({ initialNow: Date.now() })
|
|
72
|
+
}))].filter(notMissingProvider);
|
|
73
|
+
return createBaseEnvironment({
|
|
74
|
+
...baseOptions,
|
|
75
|
+
providers: [...testProviders, ...providers]
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function createMissingTransportForTest() {
|
|
79
|
+
return { async execute() {
|
|
80
|
+
throw new Error("Test environment transport was not provided.");
|
|
81
|
+
} };
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/test/idle-callback.ts
|
|
85
|
+
function createIdleCallbackForTest() {
|
|
86
|
+
let nextId = 1;
|
|
87
|
+
const callbacks = /* @__PURE__ */ new Map();
|
|
88
|
+
return {
|
|
89
|
+
requestIdleCallback(callback) {
|
|
90
|
+
const id = nextId++;
|
|
91
|
+
callbacks.set(id, callback);
|
|
92
|
+
return id;
|
|
93
|
+
},
|
|
94
|
+
cancelIdleCallback(handle) {
|
|
95
|
+
if (typeof handle === "number") callbacks.delete(handle);
|
|
96
|
+
},
|
|
97
|
+
flush() {
|
|
98
|
+
const ready = [...callbacks.entries()];
|
|
99
|
+
callbacks.clear();
|
|
100
|
+
for (const [, callback] of ready) callback();
|
|
101
|
+
},
|
|
102
|
+
get pendingCount() {
|
|
103
|
+
return callbacks.size;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region src/test/page-lifecycle.ts
|
|
109
|
+
function createPageLifecycleForTest() {
|
|
110
|
+
return { resume: createEventSubjectForTest() };
|
|
111
|
+
}
|
|
112
|
+
function createEventSubjectForTest() {
|
|
113
|
+
const observers = /* @__PURE__ */ new Set();
|
|
114
|
+
return {
|
|
115
|
+
subscribe(observer) {
|
|
116
|
+
observers.add(observer);
|
|
117
|
+
return { unsubscribe() {
|
|
118
|
+
observers.delete(observer);
|
|
119
|
+
} };
|
|
120
|
+
},
|
|
121
|
+
next(value) {
|
|
122
|
+
for (const observer of [...observers]) observer.next?.(value);
|
|
123
|
+
},
|
|
124
|
+
error(error) {
|
|
125
|
+
for (const observer of [...observers]) observer.error?.(error);
|
|
126
|
+
},
|
|
127
|
+
complete() {
|
|
128
|
+
for (const observer of [...observers]) observer.complete?.();
|
|
129
|
+
observers.clear();
|
|
130
|
+
},
|
|
131
|
+
get observerCount() {
|
|
132
|
+
return observers.size;
|
|
133
|
+
},
|
|
134
|
+
[SYMBOL_OBSERVABLE]() {
|
|
135
|
+
return this;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/test/popup.ts
|
|
141
|
+
function createPopupForTest(options = {}) {
|
|
142
|
+
const openCalls = [];
|
|
143
|
+
let attachResult = options.attachResult ?? createMissingOpenerAttachFailure();
|
|
144
|
+
return {
|
|
145
|
+
open(url, openOptions) {
|
|
146
|
+
openCalls.push({
|
|
147
|
+
url,
|
|
148
|
+
options: openOptions
|
|
149
|
+
});
|
|
150
|
+
throw new Error("Test popup open was not configured.");
|
|
151
|
+
},
|
|
152
|
+
attach() {
|
|
153
|
+
return attachResult;
|
|
154
|
+
},
|
|
155
|
+
get openCalls() {
|
|
156
|
+
return openCalls;
|
|
157
|
+
},
|
|
158
|
+
setAttachResult(result) {
|
|
159
|
+
attachResult = result;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function createMissingOpenerAttachFailure() {
|
|
164
|
+
return {
|
|
165
|
+
kind: "failure",
|
|
166
|
+
reason: PopupAttachFailureReason.MissingOpener,
|
|
167
|
+
error: new ClientError({
|
|
168
|
+
kind: ClientErrorKind.Configuration,
|
|
169
|
+
code: "test.popup.missing_opener",
|
|
170
|
+
source: "test",
|
|
171
|
+
message: "Test popup attach was not configured."
|
|
172
|
+
})
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/test/router.ts
|
|
177
|
+
function createRouterForTest(options = {}) {
|
|
178
|
+
let currentUrl = options.currentUrl == null ? null : UriReferenceString.parse(options.currentUrl);
|
|
179
|
+
const navigations = [];
|
|
180
|
+
return {
|
|
181
|
+
currentUrl() {
|
|
182
|
+
return currentUrl;
|
|
183
|
+
},
|
|
184
|
+
setCurrentUrl(url) {
|
|
185
|
+
currentUrl = url == null ? null : UriReferenceString.parse(url);
|
|
186
|
+
},
|
|
187
|
+
get navigations() {
|
|
188
|
+
return navigations;
|
|
189
|
+
},
|
|
190
|
+
async navigate(request) {
|
|
191
|
+
navigations.push(request);
|
|
192
|
+
if (request.mode === RouterNavigationMode.Push || request.mode === RouterNavigationMode.Replace) currentUrl = request.url;
|
|
193
|
+
await options.onNavigate?.(request);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region src/test/storage.ts
|
|
199
|
+
const StorageForTestCreateOptionsSchema = type({
|
|
200
|
+
initialEntries: "object",
|
|
201
|
+
validatorKey: "'persistentStorage' | 'sessionStorage'"
|
|
202
|
+
});
|
|
203
|
+
function createStorageForTest(options = {}) {
|
|
204
|
+
const { validators, ...createOptions } = options;
|
|
205
|
+
const validatorKey = options.validatorKey ?? "persistentStorage";
|
|
206
|
+
const resolvedCreateOptions = {
|
|
207
|
+
initialEntries: createOptions.initialEntries ?? {},
|
|
208
|
+
validatorKey
|
|
209
|
+
};
|
|
210
|
+
validateTraitInput({
|
|
211
|
+
value: resolvedCreateOptions,
|
|
212
|
+
bundledSchema: StorageForTestCreateOptionsSchema,
|
|
213
|
+
validator: validators?.[validatorKey],
|
|
214
|
+
onInvalid: (failure) => throwValidationClientError({
|
|
215
|
+
code: "test.storage.invalid_options",
|
|
216
|
+
source: "test",
|
|
217
|
+
messagePrefix: `createStorageForTest could not validate ${validatorKey}`,
|
|
218
|
+
failure
|
|
219
|
+
})
|
|
220
|
+
});
|
|
221
|
+
const storage = createInMemoryRecordStore();
|
|
222
|
+
for (const [key, value] of Object.entries(resolvedCreateOptions.initialEntries)) storage.set(key, value);
|
|
223
|
+
return storage;
|
|
224
|
+
}
|
|
225
|
+
//#endregion
|
|
226
|
+
//#region src/test/transport.ts
|
|
227
|
+
function createTransportForTest(options = {}) {
|
|
228
|
+
const routes = [...options.routes ?? []];
|
|
229
|
+
const history = [];
|
|
230
|
+
let fallback = options.fallback ?? createNotFoundResponseForTest();
|
|
231
|
+
return {
|
|
232
|
+
on(predicate, handler) {
|
|
233
|
+
routes.push({
|
|
234
|
+
predicate,
|
|
235
|
+
handler
|
|
236
|
+
});
|
|
237
|
+
return this;
|
|
238
|
+
},
|
|
239
|
+
onRequest(method, urlPrefix, response) {
|
|
240
|
+
return this.on((request) => request.method.toUpperCase() === method.toUpperCase() && request.url.startsWith(urlPrefix), () => response);
|
|
241
|
+
},
|
|
242
|
+
setFallback(response) {
|
|
243
|
+
fallback = response;
|
|
244
|
+
return this;
|
|
245
|
+
},
|
|
246
|
+
reset() {
|
|
247
|
+
routes.length = 0;
|
|
248
|
+
history.length = 0;
|
|
249
|
+
},
|
|
250
|
+
get history() {
|
|
251
|
+
return history;
|
|
252
|
+
},
|
|
253
|
+
async execute(request) {
|
|
254
|
+
history.push(request);
|
|
255
|
+
for (const route of routes) if (route.predicate(request)) return route.handler(request);
|
|
256
|
+
return fallback;
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function createNotFoundResponseForTest() {
|
|
261
|
+
return {
|
|
262
|
+
status: 404,
|
|
263
|
+
headers: {},
|
|
264
|
+
body: { error: "No matching test transport route" }
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
//#endregion
|
|
268
|
+
export { createEnvironmentForTest, createEventSubjectForTest, createIdleCallbackForTest, createPageLifecycleForTest, createPopupForTest, createRouterForTest, createStorageForTest, createTimeForTest, createTransportForTest };
|
|
269
|
+
|
|
270
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["defineType","defineType"],"sources":["../../src/test/time.ts","../../src/test/environment.ts","../../src/test/idle-callback.ts","../../src/test/page-lifecycle.ts","../../src/test/popup.ts","../../src/test/router.ts","../../src/test/storage.ts","../../src/test/transport.ts"],"sourcesContent":["import { type as defineType } from \"arktype\";\nimport { type EnvironmentValidators } from \"../environment/types\";\nimport { type TimeTrait } from \"../scheduling/types\";\nimport {\n\tthrowValidationClientError,\n\tvalidateTraitInput,\n\ttype WithTraitInputValidator,\n} from \"../validation\";\n\nexport interface TestTimeTrait extends TimeTrait {\n\tadvance(ms: number): void;\n\tflush(): void;\n\tadvanceAndFlush(ms: number): void;\n\tget pendingCount(): number;\n}\n\nexport interface TimeForTestCreateOptions {\n\tinitialNow?: number;\n}\n\nconst TimeForTestCreateOptionsSchema = defineType({\n\tinitialNow: \"number\",\n});\n\nexport function createTimeForTest(\n\toptions: TimeForTestCreateOptions &\n\t\tWithTraitInputValidator<Pick<EnvironmentValidators, \"time\">> = {},\n): TestTimeTrait {\n\tconst { validators, ...createOptions } = options;\n\tconst resolvedCreateOptions = {\n\t\tinitialNow: createOptions.initialNow ?? 0,\n\t};\n\tvalidateTraitInput({\n\t\tvalue: resolvedCreateOptions,\n\t\tbundledSchema: TimeForTestCreateOptionsSchema,\n\t\tvalidator: validators?.time,\n\t\tonInvalid: (failure) =>\n\t\t\tthrowValidationClientError({\n\t\t\t\tcode: \"test.time.invalid_options\",\n\t\t\t\tsource: \"test\",\n\t\t\t\tmessagePrefix: \"createTimeForTest could not validate time\",\n\t\t\t\tfailure,\n\t\t\t}),\n\t});\n\tlet now = resolvedCreateOptions.initialNow;\n\tlet nextId = 1;\n\tconst tasks = new Map<number, { at: number; handler: () => void }>();\n\treturn {\n\t\tnow: () => now,\n\t\tsetTimeout(handler, delayMs) {\n\t\t\tconst id = nextId++;\n\t\t\ttasks.set(id, { at: now + Math.max(0, delayMs), handler });\n\t\t\treturn id;\n\t\t},\n\t\tclearTimeout(handle) {\n\t\t\tif (typeof handle === \"number\") {\n\t\t\t\ttasks.delete(handle);\n\t\t\t}\n\t\t},\n\t\tadvance(ms) {\n\t\t\tnow += ms;\n\t\t},\n\t\tflush() {\n\t\t\tlet progressed = true;\n\t\t\twhile (progressed) {\n\t\t\t\tprogressed = false;\n\t\t\t\tconst ready = [...tasks.entries()]\n\t\t\t\t\t.filter(([, task]) => task.at <= now)\n\t\t\t\t\t.sort((a, b) => a[1].at - b[1].at);\n\t\t\t\tfor (const [id, task] of ready) {\n\t\t\t\t\ttasks.delete(id);\n\t\t\t\t\ttask.handler();\n\t\t\t\t\tprogressed = true;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tadvanceAndFlush(ms) {\n\t\t\tthis.advance(ms);\n\t\t\tthis.flush();\n\t\t},\n\t\tget pendingCount() {\n\t\t\treturn tasks.size;\n\t\t},\n\t};\n}\n","import {\n\ttype CreateFoundationEnvironmentOptions,\n\tcreateFoundationEnvironment,\n} from \"../environment/create\";\nimport { type FoundationEnvironment } from \"../environment/types\";\nimport {\n\tcreateProviderIfTokenMissing,\n\tgetSecuritydeptProviderToken,\n\tnotMissingProvider,\n\ttype SecuritydeptProvider,\n} from \"../injection\";\nimport { TIME_TRAIT_TOKEN, type TimeTrait } from \"../scheduling/types\";\nimport {\n\ttype BaseTransportTrait,\n\tTRANSPORT_TRAIT_TOKEN,\n} from \"../transport/types\";\nimport { createTimeForTest } from \"./time\";\n\nexport type EnvironmentCreatorOptions = {\n\tproviders?: readonly SecuritydeptProvider[];\n};\n\nexport type EnvironmentCreator<\n\tTOptions extends EnvironmentCreatorOptions,\n\tTEnvironment extends FoundationEnvironment,\n> = (options: TOptions) => TEnvironment;\n\nexport interface CreateEnvironmentForTestOptions\n\textends Omit<CreateFoundationEnvironmentOptions, \"transport\"> {\n\ttransport?: BaseTransportTrait;\n\ttime?: TimeTrait;\n}\n\nexport type CreateEnvironmentForTestWithBaseOptions<\n\tTBaseOptions extends EnvironmentCreatorOptions,\n\tTEnvironment extends FoundationEnvironment,\n> = Omit<TBaseOptions, \"providers\" | \"transport\" | \"createBaseEnvironment\"> & {\n\tcreateBaseEnvironment: EnvironmentCreator<TBaseOptions, TEnvironment>;\n\ttransport?: BaseTransportTrait;\n\tproviders?: readonly SecuritydeptProvider[];\n};\n\nexport function createEnvironmentForTest(): FoundationEnvironment;\nexport function createEnvironmentForTest(\n\toptions: CreateEnvironmentForTestOptions,\n): FoundationEnvironment;\nexport function createEnvironmentForTest<\n\tTBaseOptions extends EnvironmentCreatorOptions,\n\tTEnvironment extends FoundationEnvironment,\n>(\n\toptions: CreateEnvironmentForTestWithBaseOptions<TBaseOptions, TEnvironment>,\n): TEnvironment;\nexport function createEnvironmentForTest(\n\toptions:\n\t\t| CreateEnvironmentForTestOptions\n\t\t| CreateEnvironmentForTestWithBaseOptions<\n\t\t\t\tEnvironmentCreatorOptions,\n\t\t\t\tFoundationEnvironment\n\t\t > = {},\n): FoundationEnvironment {\n\tconst {\n\t\tcreateBaseEnvironment = createFoundationEnvironment,\n\t\ttransport,\n\t\tproviders = [],\n\t\t...baseOptions\n\t} = options as CreateEnvironmentForTestWithBaseOptions<\n\t\tEnvironmentCreatorOptions,\n\t\tFoundationEnvironment\n\t> &\n\t\tCreateEnvironmentForTestOptions;\n\tconst externalProviderTokens = new Set(\n\t\tproviders.map((provider) => getSecuritydeptProviderToken(provider)),\n\t);\n\tconst testProviders: SecuritydeptProvider[] = [\n\t\tcreateProviderIfTokenMissing(\n\t\t\texternalProviderTokens,\n\t\t\tTRANSPORT_TRAIT_TOKEN,\n\t\t\t() => ({\n\t\t\t\tprovide: TRANSPORT_TRAIT_TOKEN,\n\t\t\t\tuseValue: transport ?? createMissingTransportForTest(),\n\t\t\t}),\n\t\t),\n\t\tcreateProviderIfTokenMissing(\n\t\t\texternalProviderTokens,\n\t\t\tTIME_TRAIT_TOKEN,\n\t\t\t() => ({\n\t\t\t\tprovide: TIME_TRAIT_TOKEN,\n\t\t\t\tuseValue:\n\t\t\t\t\tbaseOptions.time ?? createTimeForTest({ initialNow: Date.now() }),\n\t\t\t}),\n\t\t),\n\t].filter(notMissingProvider) as SecuritydeptProvider[];\n\n\treturn createBaseEnvironment({\n\t\t...(baseOptions as unknown as Omit<EnvironmentCreatorOptions, \"providers\">),\n\t\tproviders: [...testProviders, ...providers],\n\t});\n}\n\nfunction createMissingTransportForTest(): BaseTransportTrait {\n\treturn {\n\t\tasync execute() {\n\t\t\tthrow new Error(\"Test environment transport was not provided.\");\n\t\t},\n\t};\n}\n","import { type IdleCallbackTrait } from \"../scheduling/types\";\n\nexport interface TestIdleCallbackTrait extends IdleCallbackTrait {\n\tflush(): void;\n\tget pendingCount(): number;\n}\n\nexport function createIdleCallbackForTest(): TestIdleCallbackTrait {\n\tlet nextId = 1;\n\tconst callbacks = new Map<number, () => void>();\n\treturn {\n\t\trequestIdleCallback(callback) {\n\t\t\tconst id = nextId++;\n\t\t\tcallbacks.set(id, callback);\n\t\t\treturn id;\n\t\t},\n\t\tcancelIdleCallback(handle) {\n\t\t\tif (typeof handle === \"number\") {\n\t\t\t\tcallbacks.delete(handle);\n\t\t\t}\n\t\t},\n\t\tflush() {\n\t\t\tconst ready = [...callbacks.entries()];\n\t\t\tcallbacks.clear();\n\t\t\tfor (const [, callback] of ready) {\n\t\t\t\tcallback();\n\t\t\t}\n\t\t},\n\t\tget pendingCount() {\n\t\t\treturn callbacks.size;\n\t\t},\n\t};\n}\n","import { SYMBOL_OBSERVABLE } from \"../compat/observable\";\nimport {\n\ttype EventObserverTrait,\n\ttype EventSubjectTrait,\n\ttype EventSubscriptionTrait,\n} from \"../events/types\";\nimport { type PageLifecycleTrait } from \"../page\";\n\nexport interface TestEventSubjectTrait<T> extends EventSubjectTrait<T> {\n\tget observerCount(): number;\n}\n\nexport interface TestPageLifecycleTrait<TResumeEvent = unknown>\n\textends PageLifecycleTrait<TResumeEvent> {\n\treadonly resume: TestEventSubjectTrait<TResumeEvent>;\n}\n\nexport function createPageLifecycleForTest<\n\tTResumeEvent = unknown,\n>(): TestPageLifecycleTrait<TResumeEvent> {\n\treturn {\n\t\tresume: createEventSubjectForTest<TResumeEvent>(),\n\t};\n}\n\nexport function createEventSubjectForTest<T>(): TestEventSubjectTrait<T> {\n\tconst observers = new Set<Partial<EventObserverTrait<T>>>();\n\tconst stream = {\n\t\tsubscribe(observer) {\n\t\t\tobservers.add(observer);\n\t\t\treturn {\n\t\t\t\tunsubscribe() {\n\t\t\t\t\tobservers.delete(observer);\n\t\t\t\t},\n\t\t\t} satisfies EventSubscriptionTrait;\n\t\t},\n\t\tnext(value) {\n\t\t\tfor (const observer of [...observers]) {\n\t\t\t\tobserver.next?.(value);\n\t\t\t}\n\t\t},\n\t\terror(error) {\n\t\t\tfor (const observer of [...observers]) {\n\t\t\t\tobserver.error?.(error);\n\t\t\t}\n\t\t},\n\t\tcomplete() {\n\t\t\tfor (const observer of [...observers]) {\n\t\t\t\tobserver.complete?.();\n\t\t\t}\n\t\t\tobservers.clear();\n\t\t},\n\t\tget observerCount() {\n\t\t\treturn observers.size;\n\t\t},\n\t\t[SYMBOL_OBSERVABLE]() {\n\t\t\treturn this;\n\t\t},\n\t} satisfies TestEventSubjectTrait<T>;\n\treturn stream;\n}\n","import { ClientError, ClientErrorKind } from \"../errors\";\nimport {\n\tPopupAttachFailureReason,\n\ttype PopupAttachResult,\n\ttype PopupOpenOptions,\n\ttype PopupTrait,\n} from \"../popup\";\n\nexport interface PopupForTestOpenCall {\n\turl: string;\n\toptions?: PopupOpenOptions;\n}\n\nexport interface PopupForTestCreateOptions {\n\tattachResult?: PopupAttachResult;\n}\n\nexport interface TestPopupTrait extends PopupTrait {\n\treadonly openCalls: readonly PopupForTestOpenCall[];\n\tsetAttachResult(result: PopupAttachResult): void;\n}\n\nexport function createPopupForTest(\n\toptions: PopupForTestCreateOptions = {},\n): TestPopupTrait {\n\tconst openCalls: PopupForTestOpenCall[] = [];\n\tlet attachResult = options.attachResult ?? createMissingOpenerAttachFailure();\n\treturn {\n\t\topen(url, openOptions) {\n\t\t\topenCalls.push({ url, options: openOptions });\n\t\t\tthrow new Error(\"Test popup open was not configured.\");\n\t\t},\n\t\tattach() {\n\t\t\treturn attachResult;\n\t\t},\n\t\tget openCalls() {\n\t\t\treturn openCalls;\n\t\t},\n\t\tsetAttachResult(result) {\n\t\t\tattachResult = result;\n\t\t},\n\t};\n}\n\nfunction createMissingOpenerAttachFailure(): PopupAttachResult {\n\treturn {\n\t\tkind: \"failure\",\n\t\treason: PopupAttachFailureReason.MissingOpener,\n\t\terror: new ClientError({\n\t\t\tkind: ClientErrorKind.Configuration,\n\t\t\tcode: \"test.popup.missing_opener\",\n\t\t\tsource: \"test\",\n\t\t\tmessage: \"Test popup attach was not configured.\",\n\t\t}),\n\t};\n}\n","import {\n\tRouterNavigationMode,\n\ttype RouterNavigationRequest,\n\ttype RouterTrait,\n} from \"../router/router\";\nimport { UriReferenceString } from \"../struct/uri-string\";\n\nexport interface RouterForTestCreateOptions {\n\tcurrentUrl?: string | UriReferenceString | null;\n\tonNavigate?: (request: RouterNavigationRequest) => void | Promise<void>;\n}\n\nexport interface TestRouterTrait extends RouterTrait {\n\treadonly navigations: readonly RouterNavigationRequest[];\n\tsetCurrentUrl(url: string | UriReferenceString | null): void;\n}\n\nexport function createRouterForTest(\n\toptions: RouterForTestCreateOptions = {},\n): TestRouterTrait {\n\tlet currentUrl =\n\t\toptions.currentUrl == null\n\t\t\t? null\n\t\t\t: UriReferenceString.parse(options.currentUrl);\n\tconst navigations: RouterNavigationRequest[] = [];\n\treturn {\n\t\tcurrentUrl() {\n\t\t\treturn currentUrl;\n\t\t},\n\t\tsetCurrentUrl(url) {\n\t\t\tcurrentUrl = url == null ? null : UriReferenceString.parse(url);\n\t\t},\n\t\tget navigations() {\n\t\t\treturn navigations;\n\t\t},\n\t\tasync navigate(request) {\n\t\t\tnavigations.push(request);\n\t\t\tif (\n\t\t\t\trequest.mode === RouterNavigationMode.Push ||\n\t\t\t\trequest.mode === RouterNavigationMode.Replace\n\t\t\t) {\n\t\t\t\tcurrentUrl = request.url;\n\t\t\t}\n\t\t\tawait options.onNavigate?.(request);\n\t\t},\n\t};\n}\n","import { type as defineType } from \"arktype\";\nimport { type EnvironmentValidators } from \"../environment/types\";\nimport { createInMemoryRecordStore } from \"../storage/memory-store\";\nimport { type SyncStorageTrait } from \"../storage/types\";\nimport {\n\tthrowValidationClientError,\n\tvalidateTraitInput,\n\ttype WithTraitInputValidator,\n} from \"../validation\";\n\nexport interface StorageForTestCreateOptions {\n\tinitialEntries?: Record<string, string>;\n\tvalidatorKey?: \"persistentStorage\" | \"sessionStorage\";\n}\n\nconst StorageForTestCreateOptionsSchema = defineType({\n\tinitialEntries: \"object\",\n\tvalidatorKey: \"'persistentStorage' | 'sessionStorage'\",\n});\n\nexport function createStorageForTest(\n\toptions: StorageForTestCreateOptions &\n\t\tWithTraitInputValidator<\n\t\t\tPick<EnvironmentValidators, \"persistentStorage\" | \"sessionStorage\">\n\t\t> = {},\n): SyncStorageTrait {\n\tconst { validators, ...createOptions } = options;\n\tconst validatorKey = options.validatorKey ?? \"persistentStorage\";\n\tconst resolvedCreateOptions = {\n\t\tinitialEntries: createOptions.initialEntries ?? {},\n\t\tvalidatorKey,\n\t};\n\tvalidateTraitInput({\n\t\tvalue: resolvedCreateOptions,\n\t\tbundledSchema: StorageForTestCreateOptionsSchema,\n\t\tvalidator: validators?.[validatorKey],\n\t\tonInvalid: (failure) =>\n\t\t\tthrowValidationClientError({\n\t\t\t\tcode: \"test.storage.invalid_options\",\n\t\t\t\tsource: \"test\",\n\t\t\t\tmessagePrefix: `createStorageForTest could not validate ${validatorKey}`,\n\t\t\t\tfailure,\n\t\t\t}),\n\t});\n\tconst storage = createInMemoryRecordStore();\n\tfor (const [key, value] of Object.entries(\n\t\tresolvedCreateOptions.initialEntries,\n\t)) {\n\t\tstorage.set(key, value);\n\t}\n\treturn storage;\n}\n","import {\n\ttype BaseTransportTrait,\n\ttype HttpRequest,\n\ttype HttpResponse,\n} from \"../transport/types\";\n\nexport interface TransportForTestRoute {\n\tpredicate: (request: HttpRequest) => boolean;\n\thandler: (request: HttpRequest) => HttpResponse | Promise<HttpResponse>;\n}\n\nexport interface TransportForTestCreateOptions {\n\troutes?: readonly TransportForTestRoute[];\n\tfallback?: HttpResponse;\n}\n\nexport interface TestTransportTrait extends BaseTransportTrait {\n\ton(\n\t\tpredicate: (request: HttpRequest) => boolean,\n\t\thandler: (request: HttpRequest) => HttpResponse | Promise<HttpResponse>,\n\t): this;\n\tonRequest(method: string, urlPrefix: string, response: HttpResponse): this;\n\tsetFallback(response: HttpResponse): this;\n\treset(): void;\n\treadonly history: readonly HttpRequest[];\n}\n\nexport function createTransportForTest(\n\toptions: TransportForTestCreateOptions = {},\n): TestTransportTrait {\n\tconst routes = [...(options.routes ?? [])];\n\tconst history: HttpRequest[] = [];\n\tlet fallback = options.fallback ?? createNotFoundResponseForTest();\n\treturn {\n\t\ton(predicate, handler) {\n\t\t\troutes.push({ predicate, handler });\n\t\t\treturn this;\n\t\t},\n\t\tonRequest(method, urlPrefix, response) {\n\t\t\treturn this.on(\n\t\t\t\t(request) =>\n\t\t\t\t\trequest.method.toUpperCase() === method.toUpperCase() &&\n\t\t\t\t\trequest.url.startsWith(urlPrefix),\n\t\t\t\t() => response,\n\t\t\t);\n\t\t},\n\t\tsetFallback(response) {\n\t\t\tfallback = response;\n\t\t\treturn this;\n\t\t},\n\t\treset() {\n\t\t\troutes.length = 0;\n\t\t\thistory.length = 0;\n\t\t},\n\t\tget history() {\n\t\t\treturn history;\n\t\t},\n\t\tasync execute(request) {\n\t\t\thistory.push(request);\n\t\t\tfor (const route of routes) {\n\t\t\t\tif (route.predicate(request)) {\n\t\t\t\t\treturn route.handler(request);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn fallback;\n\t\t},\n\t};\n}\n\nfunction createNotFoundResponseForTest(): HttpResponse {\n\treturn {\n\t\tstatus: 404,\n\t\theaders: {},\n\t\tbody: { error: \"No matching test transport route\" },\n\t};\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
//#region src/struct/uri-string.ts
|
|
2
|
+
const RELATIVE_URI_SENTINEL_BASE = "http://localhost";
|
|
3
|
+
/**
|
|
4
|
+
* Thrown when a string cannot be parsed into the requested URI variant.
|
|
5
|
+
*/
|
|
6
|
+
var UriParseError = class extends Error {
|
|
7
|
+
input;
|
|
8
|
+
kind;
|
|
9
|
+
constructor(input, kind, options) {
|
|
10
|
+
super(`Failed to parse "${input}" as ${kind}`, options);
|
|
11
|
+
this.name = "UriParseError";
|
|
12
|
+
this.input = input;
|
|
13
|
+
this.kind = kind;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Immutable wrapper around a URI-reference string.
|
|
18
|
+
*
|
|
19
|
+
* A URI-reference (RFC 3986 §4.1) is either an absolute URI or a relative
|
|
20
|
+
* reference. Concrete subclasses {@link UriString} and
|
|
21
|
+
* {@link UriRelativeString} carry the additional type-level guarantee of
|
|
22
|
+
* which variant they hold.
|
|
23
|
+
*
|
|
24
|
+
* Instances created via `UriReferenceString.parse` / `.tryParse` are always
|
|
25
|
+
* one of the two subclasses, so {@link isAbsolute} and {@link isRelative}
|
|
26
|
+
* narrow correctly.
|
|
27
|
+
*/
|
|
28
|
+
var UriReferenceString = class UriReferenceString {
|
|
29
|
+
/** The original, unmodified input string. */
|
|
30
|
+
_raw;
|
|
31
|
+
/**
|
|
32
|
+
* Eagerly parsed URL helper.
|
|
33
|
+
*
|
|
34
|
+
* - Absolute URIs: `new URL(raw)`.
|
|
35
|
+
* - Relative URIs: `new URL(raw, RELATIVE_URI_SENTINEL_BASE)`.
|
|
36
|
+
*
|
|
37
|
+
* For relative URIs the scheme and host originate from the sentinel and
|
|
38
|
+
* MUST NOT be used for origin-sensitive operations.
|
|
39
|
+
*/
|
|
40
|
+
_parsed;
|
|
41
|
+
_isAbsolute;
|
|
42
|
+
constructor(raw, parsed, isAbsolute) {
|
|
43
|
+
this._raw = raw;
|
|
44
|
+
this._parsed = parsed;
|
|
45
|
+
this._isAbsolute = isAbsolute;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Parse any valid URI reference (absolute or relative).
|
|
49
|
+
*
|
|
50
|
+
* The returned instance is always a {@link UriString} or
|
|
51
|
+
* {@link UriRelativeString}.
|
|
52
|
+
*
|
|
53
|
+
* @throws {UriParseError} when the input is not a valid URI reference.
|
|
54
|
+
*/
|
|
55
|
+
static parse(input) {
|
|
56
|
+
const result = UriReferenceString.tryParse(input);
|
|
57
|
+
if (!result) throw new UriParseError(typeof input === "string" ? input : input.toString(), "URI reference");
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Try to parse a URI reference, returning `null` on failure.
|
|
62
|
+
*
|
|
63
|
+
* The returned instance is always a {@link UriString} or
|
|
64
|
+
* {@link UriRelativeString}.
|
|
65
|
+
*/
|
|
66
|
+
static tryParse(input) {
|
|
67
|
+
if (input instanceof UriReferenceString) return input;
|
|
68
|
+
const source = typeof input === "string" ? input : input.toString();
|
|
69
|
+
return UriString.tryParse(source) ?? UriRelativeString.tryParse(source);
|
|
70
|
+
}
|
|
71
|
+
/** Create from a native `URL`. Always produces a {@link UriString}. */
|
|
72
|
+
static fromURL(url) {
|
|
73
|
+
return UriString.fromURL(url);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Create from the relative components of a URL.
|
|
77
|
+
* Always produces a {@link UriRelativeString}.
|
|
78
|
+
*/
|
|
79
|
+
static fromURLRelative(parts) {
|
|
80
|
+
return UriRelativeString.fromURLRelative(parts);
|
|
81
|
+
}
|
|
82
|
+
/** The original input string. */
|
|
83
|
+
get raw() {
|
|
84
|
+
return this._raw;
|
|
85
|
+
}
|
|
86
|
+
get pathname() {
|
|
87
|
+
return this._parsed.pathname;
|
|
88
|
+
}
|
|
89
|
+
get hash() {
|
|
90
|
+
return this._parsed.hash;
|
|
91
|
+
}
|
|
92
|
+
get search() {
|
|
93
|
+
return this._parsed.search;
|
|
94
|
+
}
|
|
95
|
+
get searchParams() {
|
|
96
|
+
return this._parsed.searchParams;
|
|
97
|
+
}
|
|
98
|
+
/** Type-guard: narrows to {@link UriString} when the reference is absolute. */
|
|
99
|
+
isAbsolute() {
|
|
100
|
+
return this._isAbsolute;
|
|
101
|
+
}
|
|
102
|
+
/** Type-guard: narrows to {@link UriRelativeString} when the reference is relative. */
|
|
103
|
+
isRelative() {
|
|
104
|
+
return !this._isAbsolute;
|
|
105
|
+
}
|
|
106
|
+
/** Narrow to {@link UriString} or return `null`. */
|
|
107
|
+
asAbsolute() {
|
|
108
|
+
return this.isAbsolute() ? this : null;
|
|
109
|
+
}
|
|
110
|
+
/** Narrow to {@link UriRelativeString} or return `null`. */
|
|
111
|
+
asRelative() {
|
|
112
|
+
return this.isRelative() ? this : null;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Returns a new instance with `hash` replaced.
|
|
116
|
+
*
|
|
117
|
+
* @param value URL-style hash, including a leading `#` when non-empty.
|
|
118
|
+
*/
|
|
119
|
+
setHash(value) {
|
|
120
|
+
const hashIndex = this._raw.indexOf("#");
|
|
121
|
+
const base = hashIndex >= 0 ? this._raw.slice(0, hashIndex) : this._raw;
|
|
122
|
+
return UriReferenceString.parse(`${base}${value}`);
|
|
123
|
+
}
|
|
124
|
+
/** Returns a new instance with the complete query parameter set replaced. */
|
|
125
|
+
setSearchParams(init) {
|
|
126
|
+
const hashIndex = this._raw.indexOf("#");
|
|
127
|
+
const hash = hashIndex >= 0 ? this._raw.slice(hashIndex) : "";
|
|
128
|
+
const withoutHash = hashIndex >= 0 ? this._raw.slice(0, hashIndex) : this._raw;
|
|
129
|
+
const searchIndex = withoutHash.indexOf("?");
|
|
130
|
+
const base = searchIndex >= 0 ? withoutHash.slice(0, searchIndex) : withoutHash;
|
|
131
|
+
const search = new URLSearchParams(init).toString();
|
|
132
|
+
return UriReferenceString.parse(`${base}${search.length > 0 ? `?${search}` : ""}${hash}`);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Convert to a native `URL`.
|
|
136
|
+
*
|
|
137
|
+
* A `base` is required when the reference is relative; it is ignored
|
|
138
|
+
* for absolute references.
|
|
139
|
+
*
|
|
140
|
+
* @throws {TypeError} when the reference is relative and no base is provided.
|
|
141
|
+
*/
|
|
142
|
+
toURL(base) {
|
|
143
|
+
if (this._isAbsolute) return new URL(this._parsed.href);
|
|
144
|
+
if (base == null) throw new TypeError("A base URL is required to convert a relative URI reference to a URL");
|
|
145
|
+
return new URL(this._raw, base);
|
|
146
|
+
}
|
|
147
|
+
toString() {
|
|
148
|
+
return this._raw;
|
|
149
|
+
}
|
|
150
|
+
valueOf() {
|
|
151
|
+
return this._raw;
|
|
152
|
+
}
|
|
153
|
+
toJSON() {
|
|
154
|
+
return this._raw;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Immutable wrapper around an absolute URI string.
|
|
159
|
+
*
|
|
160
|
+
* An absolute URI (RFC 3986 §4.3) always starts with a scheme component
|
|
161
|
+
* (e.g. `https://example.com/path`).
|
|
162
|
+
*/
|
|
163
|
+
var UriString = class UriString extends UriReferenceString {
|
|
164
|
+
constructor(raw, parsed) {
|
|
165
|
+
super(raw, parsed, true);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Parse an absolute URI string.
|
|
169
|
+
*
|
|
170
|
+
* @throws {UriParseError} when the input is not a valid absolute URI.
|
|
171
|
+
*/
|
|
172
|
+
static parse(input) {
|
|
173
|
+
const result = UriString.tryParse(input);
|
|
174
|
+
if (!result) throw new UriParseError(input, "absolute URI");
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Try to parse an absolute URI, returning `null` if the input is not
|
|
179
|
+
* a valid absolute URI.
|
|
180
|
+
*/
|
|
181
|
+
static tryParse(input) {
|
|
182
|
+
if (input instanceof UriString) return input;
|
|
183
|
+
try {
|
|
184
|
+
const parsed = new URL(input);
|
|
185
|
+
return new UriString(input, parsed);
|
|
186
|
+
} catch {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/** Create from a native `URL`, using its `href` as the raw string. */
|
|
191
|
+
static fromURL(url) {
|
|
192
|
+
return new UriString(url.href, new URL(url.href));
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Not supported — relative parts cannot produce an absolute URI.
|
|
196
|
+
*
|
|
197
|
+
* @throws {TypeError} unconditionally.
|
|
198
|
+
*/
|
|
199
|
+
static fromURLRelative(_parts) {
|
|
200
|
+
throw new TypeError("Cannot create a UriString from relative URL parts. Use UriRelativeString.fromURLRelative() or UriReferenceString.fromURLRelative() instead.");
|
|
201
|
+
}
|
|
202
|
+
get host() {
|
|
203
|
+
return this._parsed.host;
|
|
204
|
+
}
|
|
205
|
+
get hostname() {
|
|
206
|
+
return this._parsed.hostname;
|
|
207
|
+
}
|
|
208
|
+
get origin() {
|
|
209
|
+
return this._parsed.origin;
|
|
210
|
+
}
|
|
211
|
+
get password() {
|
|
212
|
+
return this._parsed.password;
|
|
213
|
+
}
|
|
214
|
+
get port() {
|
|
215
|
+
return this._parsed.port;
|
|
216
|
+
}
|
|
217
|
+
get protocol() {
|
|
218
|
+
return this._parsed.protocol;
|
|
219
|
+
}
|
|
220
|
+
get username() {
|
|
221
|
+
return this._parsed.username;
|
|
222
|
+
}
|
|
223
|
+
/** Convert to a native `URL`. No base is required for absolute URIs. */
|
|
224
|
+
toURL() {
|
|
225
|
+
return new URL(this.raw);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
/**
|
|
229
|
+
* Immutable wrapper around a relative URI reference string.
|
|
230
|
+
*
|
|
231
|
+
* A relative reference (RFC 3986 §4.2) has no scheme and must be resolved
|
|
232
|
+
* against a base URI to obtain an absolute URI.
|
|
233
|
+
*
|
|
234
|
+
* Internally, the raw string is parsed against a sentinel base
|
|
235
|
+
* (`http://localhost`) to enable eager validation and URL-component
|
|
236
|
+
* extraction. The sentinel origin is never leaked through the public API.
|
|
237
|
+
*/
|
|
238
|
+
var UriRelativeString = class UriRelativeString extends UriReferenceString {
|
|
239
|
+
constructor(raw, parsed) {
|
|
240
|
+
super(raw, parsed, false);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Parse a relative URI reference.
|
|
244
|
+
*
|
|
245
|
+
* @throws {UriParseError} when the input is absolute or not a valid
|
|
246
|
+
* relative URI reference.
|
|
247
|
+
*/
|
|
248
|
+
static parse(input) {
|
|
249
|
+
const result = UriRelativeString.tryParse(input);
|
|
250
|
+
if (!result) throw new UriParseError(input, "relative URI reference");
|
|
251
|
+
return result;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Try to parse a relative URI reference, returning `null` if the input
|
|
255
|
+
* is an absolute URI or otherwise invalid.
|
|
256
|
+
*/
|
|
257
|
+
static tryParse(input) {
|
|
258
|
+
if (input instanceof UriRelativeString) return input;
|
|
259
|
+
try {
|
|
260
|
+
new URL(input);
|
|
261
|
+
return null;
|
|
262
|
+
} catch {}
|
|
263
|
+
try {
|
|
264
|
+
const parsed = new URL(input, RELATIVE_URI_SENTINEL_BASE);
|
|
265
|
+
return new UriRelativeString(input, parsed);
|
|
266
|
+
} catch {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Not supported — a native `URL` is always absolute and cannot be
|
|
272
|
+
* represented as a relative URI reference.
|
|
273
|
+
*
|
|
274
|
+
* @throws {TypeError} unconditionally.
|
|
275
|
+
*/
|
|
276
|
+
static fromURL(_url) {
|
|
277
|
+
throw new TypeError("Cannot create a UriRelativeString from a URL; URLs are always absolute. Use UriString.fromURL() or UriReferenceString.fromURL() instead.");
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Create from the relative components of a URL.
|
|
281
|
+
*
|
|
282
|
+
* Concatenates `pathname + search + hash` into a relative URI string.
|
|
283
|
+
*/
|
|
284
|
+
static fromURLRelative(parts) {
|
|
285
|
+
const raw = `${parts.pathname}${parts.search}${parts.hash}`;
|
|
286
|
+
return UriRelativeString.parse(raw);
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Convert to a native `URL` by resolving against the given base.
|
|
290
|
+
*
|
|
291
|
+
* @param base - The base URL to resolve against (required).
|
|
292
|
+
*/
|
|
293
|
+
toURL(base) {
|
|
294
|
+
return new URL(this.raw, base);
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
//#endregion
|
|
298
|
+
export { UriString as i, UriReferenceString as n, UriRelativeString as r, UriParseError as t };
|
|
299
|
+
|
|
300
|
+
//# sourceMappingURL=uri-string-BPNbIMgv.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uri-string-BPNbIMgv.mjs","names":[],"sources":["../src/struct/uri-string.ts"],"sourcesContent":["// Sentinel base used internally to parse relative URIs via the URL constructor.\n// The specific value is never exposed; it only serves as a structural anchor\n// so that `new URL(relativeInput, SENTINEL_BASE)` succeeds.\nconst RELATIVE_URI_SENTINEL_BASE = \"http://localhost\";\n\n/**\n * Thrown when a string cannot be parsed into the requested URI variant.\n */\nexport class UriParseError extends Error {\n\treadonly input: string;\n\treadonly kind: string;\n\n\tconstructor(input: string, kind: string, options?: ErrorOptions) {\n\t\tsuper(`Failed to parse \"${input}\" as ${kind}`, options);\n\t\tthis.name = \"UriParseError\";\n\t\tthis.input = input;\n\t\tthis.kind = kind;\n\t}\n}\n\n/**\n * The relative components of a URL — pathname, search, and hash.\n *\n * All fields use the same format as the native `URL` accessors:\n * - `pathname` starts with `/` (e.g. `\"/path\"`).\n * - `search` includes the leading `?` when present (e.g. `\"?q=1\"`).\n * - `hash` includes the leading `#` when present (e.g. `\"#section\"`).\n */\nexport interface UriRelativeParts {\n\treadonly pathname: string;\n\treadonly search: string;\n\treadonly hash: string;\n}\n\n/**\n * Minimal structural type for URI fragment parsing.\n *\n * Matches any value that exposes a URL-style `hash` accessor — for example\n * `URL`, `Location`, or {@link UriReferenceString}.\n */\nexport interface UriFragmentPart {\n\treadonly hash: string;\n}\n\n/**\n * Minimal structural type for URI reference parsing.\n *\n * Matches any value that can be serialized via `toString()` — for example\n * native `URL`, `Location`, or {@link UriReferenceString}.\n */\nexport interface UriReferenceStringLike {\n\ttoString(): string;\n}\n\nexport type UriReferenceStringInput = string | UriReferenceStringLike;\n\nexport type UriSearchParamsInit = NonNullable<\n\tConstructorParameters<typeof URLSearchParams>[0]\n>;\n\n// ---------------------------------------------------------------------------\n// UriReferenceString — the URI-reference supertype (absolute | relative)\n// ---------------------------------------------------------------------------\n\n/**\n * Immutable wrapper around a URI-reference string.\n *\n * A URI-reference (RFC 3986 §4.1) is either an absolute URI or a relative\n * reference. Concrete subclasses {@link UriString} and\n * {@link UriRelativeString} carry the additional type-level guarantee of\n * which variant they hold.\n *\n * Instances created via `UriReferenceString.parse` / `.tryParse` are always\n * one of the two subclasses, so {@link isAbsolute} and {@link isRelative}\n * narrow correctly.\n */\nexport class UriReferenceString {\n\t/** The original, unmodified input string. */\n\tprotected readonly _raw: string;\n\n\t/**\n\t * Eagerly parsed URL helper.\n\t *\n\t * - Absolute URIs: `new URL(raw)`.\n\t * - Relative URIs: `new URL(raw, RELATIVE_URI_SENTINEL_BASE)`.\n\t *\n\t * For relative URIs the scheme and host originate from the sentinel and\n\t * MUST NOT be used for origin-sensitive operations.\n\t */\n\tprotected readonly _parsed: URL;\n\n\tprotected readonly _isAbsolute: boolean;\n\n\tprotected constructor(raw: string, parsed: URL, isAbsolute: boolean) {\n\t\tthis._raw = raw;\n\t\tthis._parsed = parsed;\n\t\tthis._isAbsolute = isAbsolute;\n\t}\n\n\t// ---- Static factories ------------------------------------------------\n\n\t/**\n\t * Parse any valid URI reference (absolute or relative).\n\t *\n\t * The returned instance is always a {@link UriString} or\n\t * {@link UriRelativeString}.\n\t *\n\t * @throws {UriParseError} when the input is not a valid URI reference.\n\t */\n\tstatic parse(input: UriReferenceStringInput): UriReferenceString {\n\t\tconst result = UriReferenceString.tryParse(input);\n\t\tif (!result) {\n\t\t\tthrow new UriParseError(\n\t\t\t\ttypeof input === \"string\" ? input : input.toString(),\n\t\t\t\t\"URI reference\",\n\t\t\t);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Try to parse a URI reference, returning `null` on failure.\n\t *\n\t * The returned instance is always a {@link UriString} or\n\t * {@link UriRelativeString}.\n\t */\n\tstatic tryParse(input: UriReferenceStringInput): UriReferenceString | null {\n\t\tif (input instanceof UriReferenceString) {\n\t\t\treturn input;\n\t\t}\n\t\tconst source = typeof input === \"string\" ? input : input.toString();\n\t\treturn UriString.tryParse(source) ?? UriRelativeString.tryParse(source);\n\t}\n\n\t/** Create from a native `URL`. Always produces a {@link UriString}. */\n\tstatic fromURL(url: URL): UriString {\n\t\treturn UriString.fromURL(url);\n\t}\n\n\t/**\n\t * Create from the relative components of a URL.\n\t * Always produces a {@link UriRelativeString}.\n\t */\n\tstatic fromURLRelative(parts: UriRelativeParts): UriRelativeString {\n\t\treturn UriRelativeString.fromURLRelative(parts);\n\t}\n\n\t// ---- Accessors -------------------------------------------------------\n\n\t/** The original input string. */\n\tget raw(): string {\n\t\treturn this._raw;\n\t}\n\n\tget pathname(): string {\n\t\treturn this._parsed.pathname;\n\t}\n\n\tget hash(): string {\n\t\treturn this._parsed.hash;\n\t}\n\n\tget search(): string {\n\t\treturn this._parsed.search;\n\t}\n\n\tget searchParams(): URLSearchParams {\n\t\treturn this._parsed.searchParams;\n\t}\n\n\t// ---- Type narrowing --------------------------------------------------\n\n\t/** Type-guard: narrows to {@link UriString} when the reference is absolute. */\n\tisAbsolute(): this is UriString {\n\t\treturn this._isAbsolute;\n\t}\n\n\t/** Type-guard: narrows to {@link UriRelativeString} when the reference is relative. */\n\tisRelative(): this is UriRelativeString {\n\t\treturn !this._isAbsolute;\n\t}\n\n\t/** Narrow to {@link UriString} or return `null`. */\n\tasAbsolute(): UriString | null {\n\t\treturn this.isAbsolute() ? this : null;\n\t}\n\n\t/** Narrow to {@link UriRelativeString} or return `null`. */\n\tasRelative(): UriRelativeString | null {\n\t\treturn this.isRelative() ? this : null;\n\t}\n\n\t// ---- Mutation (immutable) --------------------------------------------\n\n\t/**\n\t * Returns a new instance with `hash` replaced.\n\t *\n\t * @param value URL-style hash, including a leading `#` when non-empty.\n\t */\n\tsetHash(value: string): this {\n\t\tconst hashIndex = this._raw.indexOf(\"#\");\n\t\tconst base = hashIndex >= 0 ? this._raw.slice(0, hashIndex) : this._raw;\n\t\treturn UriReferenceString.parse(`${base}${value}`) as this;\n\t}\n\n\t/** Returns a new instance with the complete query parameter set replaced. */\n\tsetSearchParams(init?: UriSearchParamsInit): this {\n\t\tconst hashIndex = this._raw.indexOf(\"#\");\n\t\tconst hash = hashIndex >= 0 ? this._raw.slice(hashIndex) : \"\";\n\t\tconst withoutHash =\n\t\t\thashIndex >= 0 ? this._raw.slice(0, hashIndex) : this._raw;\n\t\tconst searchIndex = withoutHash.indexOf(\"?\");\n\t\tconst base =\n\t\t\tsearchIndex >= 0 ? withoutHash.slice(0, searchIndex) : withoutHash;\n\t\tconst search = new URLSearchParams(init).toString();\n\t\treturn UriReferenceString.parse(\n\t\t\t`${base}${search.length > 0 ? `?${search}` : \"\"}${hash}`,\n\t\t) as this;\n\t}\n\n\t// ---- Conversion ------------------------------------------------------\n\n\t/**\n\t * Convert to a native `URL`.\n\t *\n\t * A `base` is required when the reference is relative; it is ignored\n\t * for absolute references.\n\t *\n\t * @throws {TypeError} when the reference is relative and no base is provided.\n\t */\n\ttoURL(base?: string | URL): URL {\n\t\tif (this._isAbsolute) {\n\t\t\treturn new URL(this._parsed.href);\n\t\t}\n\t\tif (base == null) {\n\t\t\tthrow new TypeError(\n\t\t\t\t\"A base URL is required to convert a relative URI reference to a URL\",\n\t\t\t);\n\t\t}\n\t\treturn new URL(this._raw, base);\n\t}\n\n\ttoString(): string {\n\t\treturn this._raw;\n\t}\n\n\tvalueOf(): string {\n\t\treturn this._raw;\n\t}\n\n\ttoJSON(): string {\n\t\treturn this._raw;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// UriString — absolute URI (must include a scheme)\n// ---------------------------------------------------------------------------\n\n/**\n * Immutable wrapper around an absolute URI string.\n *\n * An absolute URI (RFC 3986 §4.3) always starts with a scheme component\n * (e.g. `https://example.com/path`).\n */\nexport class UriString extends UriReferenceString {\n\tprotected constructor(raw: string, parsed: URL) {\n\t\tsuper(raw, parsed, true);\n\t}\n\n\t// ---- Static factories ------------------------------------------------\n\n\t/**\n\t * Parse an absolute URI string.\n\t *\n\t * @throws {UriParseError} when the input is not a valid absolute URI.\n\t */\n\tstatic override parse(input: string): UriString {\n\t\tconst result = UriString.tryParse(input);\n\t\tif (!result) {\n\t\t\tthrow new UriParseError(input, \"absolute URI\");\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Try to parse an absolute URI, returning `null` if the input is not\n\t * a valid absolute URI.\n\t */\n\tstatic override tryParse(input: string | UriString): UriString | null {\n\t\tif (input instanceof UriString) {\n\t\t\treturn input;\n\t\t}\n\t\ttry {\n\t\t\tconst parsed = new URL(input);\n\t\t\treturn new UriString(input, parsed);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/** Create from a native `URL`, using its `href` as the raw string. */\n\tstatic override fromURL(url: URL): UriString {\n\t\treturn new UriString(url.href, new URL(url.href));\n\t}\n\n\t/**\n\t * Not supported — relative parts cannot produce an absolute URI.\n\t *\n\t * @throws {TypeError} unconditionally.\n\t */\n\tstatic override fromURLRelative(_parts: UriRelativeParts): never {\n\t\tthrow new TypeError(\n\t\t\t\"Cannot create a UriString from relative URL parts. \" +\n\t\t\t\t\"Use UriRelativeString.fromURLRelative() or UriReferenceString.fromURLRelative() instead.\",\n\t\t);\n\t}\n\n\t// ---- Accessors -------------------------------------------------------\n\n\tget host(): string {\n\t\treturn this._parsed.host;\n\t}\n\n\tget hostname(): string {\n\t\treturn this._parsed.hostname;\n\t}\n\n\tget origin(): string {\n\t\treturn this._parsed.origin;\n\t}\n\n\tget password(): string {\n\t\treturn this._parsed.password;\n\t}\n\n\tget port(): string {\n\t\treturn this._parsed.port;\n\t}\n\n\tget protocol(): string {\n\t\treturn this._parsed.protocol;\n\t}\n\n\tget username(): string {\n\t\treturn this._parsed.username;\n\t}\n\n\t// ---- Conversion ------------------------------------------------------\n\n\t/** Convert to a native `URL`. No base is required for absolute URIs. */\n\toverride toURL(): URL {\n\t\treturn new URL(this.raw);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// UriRelativeString — relative URI reference (no scheme)\n// ---------------------------------------------------------------------------\n\n/**\n * Immutable wrapper around a relative URI reference string.\n *\n * A relative reference (RFC 3986 §4.2) has no scheme and must be resolved\n * against a base URI to obtain an absolute URI.\n *\n * Internally, the raw string is parsed against a sentinel base\n * (`http://localhost`) to enable eager validation and URL-component\n * extraction. The sentinel origin is never leaked through the public API.\n */\nexport class UriRelativeString extends UriReferenceString {\n\tprotected constructor(raw: string, parsed: URL) {\n\t\tsuper(raw, parsed, false);\n\t}\n\n\t// ---- Static factories ------------------------------------------------\n\n\t/**\n\t * Parse a relative URI reference.\n\t *\n\t * @throws {UriParseError} when the input is absolute or not a valid\n\t * relative URI reference.\n\t */\n\tstatic override parse(input: string): UriRelativeString {\n\t\tconst result = UriRelativeString.tryParse(input);\n\t\tif (!result) {\n\t\t\tthrow new UriParseError(input, \"relative URI reference\");\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Try to parse a relative URI reference, returning `null` if the input\n\t * is an absolute URI or otherwise invalid.\n\t */\n\tstatic override tryParse(\n\t\tinput: string | UriRelativeString,\n\t): UriRelativeString | null {\n\t\tif (input instanceof UriRelativeString) {\n\t\t\treturn input;\n\t\t}\n\t\t// Reject absolute URIs — they belong in UriString.\n\t\ttry {\n\t\t\tnew URL(input);\n\t\t\treturn null;\n\t\t} catch {\n\t\t\t// Not absolute — fall through to relative parsing.\n\t\t}\n\t\ttry {\n\t\t\tconst parsed = new URL(input, RELATIVE_URI_SENTINEL_BASE);\n\t\t\treturn new UriRelativeString(input, parsed);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Not supported — a native `URL` is always absolute and cannot be\n\t * represented as a relative URI reference.\n\t *\n\t * @throws {TypeError} unconditionally.\n\t */\n\tstatic override fromURL(_url: URL): never {\n\t\tthrow new TypeError(\n\t\t\t\"Cannot create a UriRelativeString from a URL; URLs are always absolute. \" +\n\t\t\t\t\"Use UriString.fromURL() or UriReferenceString.fromURL() instead.\",\n\t\t);\n\t}\n\n\t/**\n\t * Create from the relative components of a URL.\n\t *\n\t * Concatenates `pathname + search + hash` into a relative URI string.\n\t */\n\tstatic override fromURLRelative(parts: UriRelativeParts): UriRelativeString {\n\t\tconst raw = `${parts.pathname}${parts.search}${parts.hash}`;\n\t\treturn UriRelativeString.parse(raw);\n\t}\n\n\t// ---- Conversion ------------------------------------------------------\n\n\t/**\n\t * Convert to a native `URL` by resolving against the given base.\n\t *\n\t * @param base - The base URL to resolve against (required).\n\t */\n\toverride toURL(base: string | URL): URL {\n\t\treturn new URL(this.raw, base);\n\t}\n}\n"],"mappings":"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"}
|