@prosopo/client-example-server 2.7.122 → 2.7.124

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +15 -31
  2. package/.turbo/turbo-build$colon$tsc.log +21 -21
  3. package/.turbo/turbo-build.log +16 -31
  4. package/CHANGELOG.md +35 -0
  5. package/dist/_virtual/_rolldown/runtime.js +3 -0
  6. package/dist/app.d.ts +15 -1
  7. package/dist/app.d.ts.map +1 -1
  8. package/dist/app.js +122 -93
  9. package/dist/app.js.map +1 -1
  10. package/dist/cjs/_virtual/_rolldown/runtime.cjs +23 -0
  11. package/dist/cjs/app.cjs +148 -106
  12. package/dist/cjs/controllers/auth.cjs +180 -244
  13. package/dist/cjs/models/user.cjs +21 -22
  14. package/dist/cjs/routes/routes.cjs +21 -24
  15. package/dist/cjs/utils/connection.cjs +20 -23
  16. package/dist/cjs/utils/database.cjs +3 -28
  17. package/dist/controllers/auth.d.ts +1 -1
  18. package/dist/controllers/auth.d.ts.map +1 -1
  19. package/dist/controllers/auth.js +173 -241
  20. package/dist/controllers/auth.js.map +1 -1
  21. package/dist/models/user.d.ts +2 -2
  22. package/dist/models/user.js +18 -22
  23. package/dist/routes/routes.d.ts.map +1 -1
  24. package/dist/routes/routes.js +18 -24
  25. package/dist/routes/routes.js.map +1 -1
  26. package/dist/tests/app.unit.test.d.ts +2 -0
  27. package/dist/tests/app.unit.test.d.ts.map +1 -0
  28. package/dist/tests/app.unit.test.js +318 -0
  29. package/dist/tests/app.unit.test.js.map +1 -0
  30. package/dist/tests/appEntrypoint.unit.test.d.ts +2 -0
  31. package/dist/tests/appEntrypoint.unit.test.d.ts.map +1 -0
  32. package/dist/tests/appEntrypoint.unit.test.js +100 -0
  33. package/dist/tests/appEntrypoint.unit.test.js.map +1 -0
  34. package/dist/tests/auth.unit.test.d.ts +2 -0
  35. package/dist/tests/auth.unit.test.d.ts.map +1 -0
  36. package/dist/tests/auth.unit.test.js +426 -0
  37. package/dist/tests/auth.unit.test.js.map +1 -0
  38. package/dist/tests/authHarness.d.ts +40 -0
  39. package/dist/tests/authHarness.d.ts.map +1 -0
  40. package/dist/tests/authHarness.js +57 -0
  41. package/dist/tests/authHarness.js.map +1 -0
  42. package/dist/tests/clientExampleServer.test-d.d.ts +2 -0
  43. package/dist/tests/clientExampleServer.test-d.d.ts.map +1 -0
  44. package/dist/tests/clientExampleServer.test-d.js +84 -0
  45. package/dist/tests/clientExampleServer.test-d.js.map +1 -0
  46. package/dist/tests/routes.unit.test.d.ts +2 -0
  47. package/dist/tests/routes.unit.test.d.ts.map +1 -0
  48. package/dist/tests/routes.unit.test.js +123 -0
  49. package/dist/tests/routes.unit.test.js.map +1 -0
  50. package/dist/tests/utils.unit.test.d.ts +2 -0
  51. package/dist/tests/utils.unit.test.d.ts.map +1 -0
  52. package/dist/tests/utils.unit.test.js +125 -0
  53. package/dist/tests/utils.unit.test.js.map +1 -0
  54. package/dist/utils/connection.js +14 -19
  55. package/dist/utils/database.js +4 -8
  56. package/package.json +17 -14
  57. package/src/app.ts +114 -73
  58. package/src/controllers/auth.ts +40 -31
  59. package/src/routes/routes.ts +5 -2
  60. package/src/tests/app.unit.test.ts +428 -0
  61. package/src/tests/appEntrypoint.unit.test.ts +154 -0
  62. package/src/tests/auth.unit.test.ts +859 -0
  63. package/src/tests/authHarness.ts +131 -0
  64. package/src/tests/clientExampleServer.test-d.ts +146 -0
  65. package/src/tests/routes.unit.test.ts +166 -0
  66. package/src/tests/utils.unit.test.ts +169 -0
  67. package/tsconfig.tsbuildinfo +1 -1
  68. package/vite.test.config.ts +18 -0
@@ -0,0 +1,318 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import express from "express";
5
+ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
6
+ import { DEFAULT_PORT, ProsopoVerificationType, createApp, isDevelopmentOrTest, main, resolvePort, resolveVerifyEndpoint, resolveVerifyType, startServer, } from "../app.js";
7
+ import { serverConfig } from "./authHarness.js";
8
+ const mocks = vi.hoisted(() => ({
9
+ memoryUris: [],
10
+ memorySetup: vi.fn(),
11
+ connections: [],
12
+ routesArgs: [],
13
+ config: {},
14
+ }));
15
+ vi.mock("../utils/database.js", () => ({
16
+ default: () => {
17
+ mocks.memorySetup();
18
+ return Promise.resolve("mongodb://memory:27017/");
19
+ },
20
+ }));
21
+ vi.mock("../utils/connection.js", () => ({
22
+ default: (uri) => {
23
+ mocks.connections.push(uri);
24
+ return {};
25
+ },
26
+ }));
27
+ vi.mock("../routes/routes.js", () => ({
28
+ default: (...args) => {
29
+ mocks.routesArgs.push(args);
30
+ return express.Router();
31
+ },
32
+ }));
33
+ vi.mock("@prosopo/server", async (importOriginal) => {
34
+ const actual = await importOriginal();
35
+ return {
36
+ ...actual,
37
+ getServerConfig: () => mocks.config,
38
+ };
39
+ });
40
+ vi.mock("@prosopo/dotenv", async (importOriginal) => {
41
+ const actual = await importOriginal();
42
+ return {
43
+ ...actual,
44
+ loadEnv: () => undefined,
45
+ };
46
+ });
47
+ const servers = [];
48
+ const track = (server) => {
49
+ servers.push(server);
50
+ return server;
51
+ };
52
+ const env = { ...process.env };
53
+ beforeEach(() => {
54
+ mocks.memorySetup.mockClear();
55
+ mocks.memoryUris.length = 0;
56
+ mocks.connections.length = 0;
57
+ mocks.routesArgs.length = 0;
58
+ mocks.config = serverConfig();
59
+ process.env.NODE_ENV = "test";
60
+ process.env.MONGO_URI = "mongodb://configured:27017/";
61
+ process.env.PROSOPO_SITE_PRIVATE_KEY = "//Alice";
62
+ Reflect.deleteProperty(process.env, "PROSOPO_VERIFY_ENDPOINT");
63
+ Reflect.deleteProperty(process.env, "PROSOPO_VERIFICATION_TYPE");
64
+ });
65
+ afterEach(async () => {
66
+ await Promise.all(servers.splice(0).map((server) => new Promise((resolve) => {
67
+ server.close(() => resolve());
68
+ })));
69
+ for (const key of Object.keys(process.env)) {
70
+ if (!(key in env))
71
+ Reflect.deleteProperty(process.env, key);
72
+ }
73
+ Object.assign(process.env, env);
74
+ vi.restoreAllMocks();
75
+ });
76
+ describe("resolveVerifyEndpoint", () => {
77
+ test("prefers an explicitly configured endpoint", () => {
78
+ process.env.PROSOPO_VERIFY_ENDPOINT = "https://self-hosted/siteverify";
79
+ expect(resolveVerifyEndpoint()).toBe("https://self-hosted/siteverify");
80
+ });
81
+ test("prefixes the hosted API with the environment name", () => {
82
+ process.env.NODE_ENV = "staging";
83
+ expect(resolveVerifyEndpoint()).toBe("https://staging-api.prosopo.io/siteverify");
84
+ });
85
+ test("uses the bare hosted API in production", () => {
86
+ process.env.NODE_ENV = "production";
87
+ expect(resolveVerifyEndpoint()).toBe("https://api.prosopo.io/siteverify");
88
+ });
89
+ test("an unset NODE_ENV falls back to the production host", () => {
90
+ Reflect.deleteProperty(process.env, "NODE_ENV");
91
+ expect(resolveVerifyEndpoint()).toBe("https://api.prosopo.io/siteverify");
92
+ });
93
+ test("an empty NODE_ENV is treated the same way", () => {
94
+ process.env.NODE_ENV = "";
95
+ expect(resolveVerifyEndpoint()).toBe("https://api.prosopo.io/siteverify");
96
+ });
97
+ });
98
+ describe("resolveVerifyType", () => {
99
+ test("accepts the verification types it knows", () => {
100
+ process.env.PROSOPO_VERIFICATION_TYPE = "local";
101
+ expect(resolveVerifyType()).toBe(ProsopoVerificationType.local);
102
+ process.env.PROSOPO_VERIFICATION_TYPE = "api";
103
+ expect(resolveVerifyType()).toBe(ProsopoVerificationType.api);
104
+ });
105
+ test("falls back to the hosted API for anything else", () => {
106
+ process.env.PROSOPO_VERIFICATION_TYPE = "carrier-pigeon";
107
+ expect(resolveVerifyType()).toBe(ProsopoVerificationType.api);
108
+ });
109
+ test("falls back to the hosted API when unset", () => {
110
+ expect(resolveVerifyType()).toBe(ProsopoVerificationType.api);
111
+ });
112
+ });
113
+ describe("isDevelopmentOrTest", () => {
114
+ test("is true only for development and test", () => {
115
+ for (const [value, expected] of [
116
+ ["development", true],
117
+ ["test", true],
118
+ ["production", false],
119
+ ["staging", false],
120
+ ]) {
121
+ process.env.NODE_ENV = value;
122
+ expect(isDevelopmentOrTest()).toBe(expected);
123
+ }
124
+ });
125
+ });
126
+ describe("resolvePort", () => {
127
+ test("takes the port from the configured server URL", () => {
128
+ expect(resolvePort(serverConfig())).toBe(9228);
129
+ });
130
+ test("defaults when no server URL is configured", () => {
131
+ const config = {
132
+ ...serverConfig(),
133
+ serverUrl: "",
134
+ };
135
+ expect(resolvePort(config)).toBe(9228);
136
+ });
137
+ test("defaults on a server URL with no port segment", () => {
138
+ const config = {
139
+ ...serverConfig(),
140
+ serverUrl: "https://localhost",
141
+ };
142
+ expect(resolvePort(config)).toBe(DEFAULT_PORT);
143
+ });
144
+ test("defaults when the port segment isn't a number", () => {
145
+ const config = {
146
+ ...serverConfig(),
147
+ serverUrl: "https://localhost:https",
148
+ };
149
+ expect(resolvePort(config)).toBe(DEFAULT_PORT);
150
+ });
151
+ });
152
+ describe("createApp", () => {
153
+ const listen = (app) => new Promise((resolve) => {
154
+ const server = track(app.listen(0, () => {
155
+ const { port } = server.address();
156
+ resolve(`http://127.0.0.1:${port}`);
157
+ }));
158
+ });
159
+ test("sets permissive CORS headers on every response", async () => {
160
+ const app = createApp();
161
+ app.get("/thing", (_req, res) => {
162
+ res.status(200).json({ ok: true });
163
+ });
164
+ const base = await listen(app);
165
+ const response = await fetch(`${base}/thing`);
166
+ expect(response.headers.get("access-control-allow-origin")).toBe("*");
167
+ expect(response.headers.get("access-control-allow-methods")).toContain("POST");
168
+ expect(response.headers.get("access-control-allow-headers")).toContain("Authorization");
169
+ });
170
+ test("answers preflight requests", async () => {
171
+ const base = await listen(createApp());
172
+ const response = await fetch(`${base}/anything`, { method: "OPTIONS" });
173
+ expect(response.status).toBe(204);
174
+ expect(response.headers.get("access-control-allow-methods")).toContain("POST");
175
+ });
176
+ test("parses JSON bodies", async () => {
177
+ const app = createApp();
178
+ app.post("/echo", (req, res) => {
179
+ res.status(200).json(req.body);
180
+ });
181
+ const base = await listen(app);
182
+ const response = await fetch(`${base}/echo`, {
183
+ method: "POST",
184
+ headers: { "content-type": "application/json" },
185
+ body: JSON.stringify({ email: "user@example.com" }),
186
+ });
187
+ expect(await response.json()).toEqual({ email: "user@example.com" });
188
+ });
189
+ test("parses form-encoded bodies", async () => {
190
+ const app = createApp();
191
+ app.post("/echo", (req, res) => {
192
+ res.status(200).json(req.body);
193
+ });
194
+ const base = await listen(app);
195
+ const response = await fetch(`${base}/echo`, {
196
+ method: "POST",
197
+ headers: { "content-type": "application/x-www-form-urlencoded" },
198
+ body: "email=user%40example.com",
199
+ });
200
+ expect(await response.json()).toEqual({ email: "user@example.com" });
201
+ });
202
+ test("trusts one proxy hop, so client IPs survive the reverse proxy", () => {
203
+ expect(createApp().get("trust proxy")).toBe(1);
204
+ });
205
+ });
206
+ describe("startServer", () => {
207
+ const certDir = () => {
208
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "ces-certs-"));
209
+ fs.mkdirSync(path.join(root, "certs"));
210
+ return root;
211
+ };
212
+ test("serves HTTPS in development when the certificates exist", async () => {
213
+ process.env.NODE_ENV = "development";
214
+ const root = certDir();
215
+ const moduleDir = path.join(root, "certs", "a", "b");
216
+ fs.mkdirSync(moduleDir, { recursive: true });
217
+ const certs = path.join(root, "certs");
218
+ fs.writeFileSync(path.join(certs, "server.key"), "key");
219
+ fs.writeFileSync(path.join(certs, "server.crt"), "cert");
220
+ const https = await import("node:https");
221
+ const fake = { listen: vi.fn((_port, cb) => cb()) };
222
+ const createServer = vi
223
+ .spyOn(https.default, "createServer")
224
+ .mockReturnValue(fake);
225
+ startServer(express(), 1234, path.join(certs, "a", "b"));
226
+ expect(createServer).toHaveBeenCalledTimes(1);
227
+ const options = createServer.mock.calls[0]?.[0];
228
+ expect(options.key.toString()).toBe("key");
229
+ expect(options.cert.toString()).toBe("cert");
230
+ fs.rmSync(root, { recursive: true, force: true });
231
+ });
232
+ test("falls back to HTTP in development when the certificates are missing", async () => {
233
+ process.env.NODE_ENV = "development";
234
+ const server = track(startServer(express(), 0, path.join(os.tmpdir(), "no-such-dir")));
235
+ await new Promise((resolve) => {
236
+ if (server.listening)
237
+ return resolve();
238
+ server.once("listening", () => resolve());
239
+ });
240
+ expect(server.address().port).toBeGreaterThan(0);
241
+ });
242
+ test("serves plain HTTP in production, where a proxy terminates TLS", async () => {
243
+ process.env.NODE_ENV = "production";
244
+ const https = await import("node:https");
245
+ const createServer = vi.spyOn(https.default, "createServer");
246
+ const server = track(startServer(express(), 0));
247
+ expect(createServer).not.toHaveBeenCalled();
248
+ expect(server.address().port).toBeGreaterThan(0);
249
+ });
250
+ test("the server it returns actually serves the app", async () => {
251
+ process.env.NODE_ENV = "production";
252
+ const app = express();
253
+ app.get("/health", (_req, res) => {
254
+ res.status(200).json({ status: "ok" });
255
+ });
256
+ const server = track(startServer(app, 0));
257
+ await new Promise((resolve) => {
258
+ if (server.listening)
259
+ return resolve();
260
+ server.once("listening", () => resolve());
261
+ });
262
+ const { port } = server.address();
263
+ const response = await fetch(`http://127.0.0.1:${port}/health`);
264
+ expect(await response.json()).toEqual({ status: "ok" });
265
+ });
266
+ });
267
+ describe("main", () => {
268
+ test("wires the routes with the resolved endpoint and verification type", async () => {
269
+ process.env.PROSOPO_VERIFY_ENDPOINT = "https://self-hosted/siteverify";
270
+ process.env.PROSOPO_VERIFICATION_TYPE = "local";
271
+ mocks.config = { ...serverConfig(), serverUrl: "https://localhost:0" };
272
+ track(await main());
273
+ expect(mocks.routesArgs[0]?.slice(1)).toEqual([
274
+ mocks.config,
275
+ "https://self-hosted/siteverify",
276
+ ProsopoVerificationType.local,
277
+ ]);
278
+ });
279
+ test("connects to the configured mongo instance", async () => {
280
+ mocks.config = { ...serverConfig(), serverUrl: "https://localhost:0" };
281
+ track(await main());
282
+ expect(mocks.connections).toEqual(["mongodb://configured:27017/"]);
283
+ expect(mocks.memorySetup).not.toHaveBeenCalled();
284
+ });
285
+ test("starts an in-memory mongo when none is configured", async () => {
286
+ Reflect.deleteProperty(process.env, "MONGO_URI");
287
+ mocks.config = { ...serverConfig(), serverUrl: "https://localhost:0" };
288
+ track(await main());
289
+ expect(mocks.memorySetup).toHaveBeenCalledTimes(1);
290
+ expect(mocks.connections).toEqual(["mongodb://memory:27017/"]);
291
+ });
292
+ test("refuses to run an in-memory database outside development", async () => {
293
+ Reflect.deleteProperty(process.env, "MONGO_URI");
294
+ process.env.NODE_ENV = "production";
295
+ await expect(main()).rejects.toThrow("Cannot run mongo memory when NODE_ENV is neither development nor test");
296
+ expect(mocks.connections).toHaveLength(0);
297
+ });
298
+ test("boots without a site private key, but says so", async () => {
299
+ Reflect.deleteProperty(process.env, "PROSOPO_SITE_PRIVATE_KEY");
300
+ mocks.config = { ...serverConfig(), serverUrl: "https://localhost:0" };
301
+ const server = track(await main());
302
+ expect(server.listening).toBe(true);
303
+ });
304
+ test("listens on the port the config names", async () => {
305
+ mocks.config = { ...serverConfig(), serverUrl: "https://localhost:0" };
306
+ const server = track(await main());
307
+ expect(server.address().port).toBeGreaterThan(0);
308
+ });
309
+ test("propagates a database connection failure instead of listening", async () => {
310
+ mocks.config = { ...serverConfig(), serverUrl: "https://localhost:0" };
311
+ const connection = await import("../utils/connection.js");
312
+ vi.spyOn(connection, "default").mockImplementation(() => {
313
+ throw new Error("mongo unreachable");
314
+ });
315
+ await expect(main()).rejects.toThrow("mongo unreachable");
316
+ });
317
+ });
318
+ //# sourceMappingURL=app.unit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.unit.test.js","sourceRoot":"","sources":["../../src/tests/app.unit.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=appEntrypoint.unit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appEntrypoint.unit.test.d.ts","sourceRoot":"","sources":["../../src/tests/appEntrypoint.unit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,100 @@
1
+ import express from "express";
2
+ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
3
+ import { serverConfig } from "./authHarness.js";
4
+ const mocks = vi.hoisted(() => ({
5
+ isMain: true,
6
+ servers: [],
7
+ connectionThrows: undefined,
8
+ config: {},
9
+ }));
10
+ vi.mock("@prosopo/util", async (importOriginal) => {
11
+ const actual = await importOriginal();
12
+ return {
13
+ ...actual,
14
+ isMain: () => mocks.isMain,
15
+ };
16
+ });
17
+ vi.mock("../utils/connection.js", () => ({
18
+ default: () => {
19
+ if (mocks.connectionThrows)
20
+ throw mocks.connectionThrows;
21
+ return {};
22
+ },
23
+ }));
24
+ vi.mock("../utils/database.js", () => ({
25
+ default: () => Promise.resolve("mongodb://memory:27017/"),
26
+ }));
27
+ vi.mock("../routes/routes.js", () => ({
28
+ default: () => express.Router(),
29
+ }));
30
+ vi.mock("@prosopo/server", async (importOriginal) => {
31
+ const actual = await importOriginal();
32
+ return { ...actual, getServerConfig: () => mocks.config };
33
+ });
34
+ vi.mock("@prosopo/dotenv", async (importOriginal) => {
35
+ const actual = await importOriginal();
36
+ return { ...actual, loadEnv: () => undefined };
37
+ });
38
+ const importApp = async (options = {}) => {
39
+ vi.resetModules();
40
+ const listeners = [];
41
+ const httpModule = await import("node:http");
42
+ const httpsModule = await import("node:https");
43
+ const createServer = httpModule.default.createServer;
44
+ vi.spyOn(httpModule.default, "createServer").mockImplementation((...args) => {
45
+ const server = createServer(...args);
46
+ listeners.push(server);
47
+ mocks.servers.push(server);
48
+ return server;
49
+ });
50
+ const createSecureServer = httpsModule.default.createServer;
51
+ vi.spyOn(httpsModule.default, "createServer").mockImplementation((...args) => {
52
+ const server = createSecureServer(...args);
53
+ listeners.push(server);
54
+ mocks.servers.push(server);
55
+ return server;
56
+ });
57
+ await import("../app.js");
58
+ for (let attempt = 0; attempt < 100; attempt++) {
59
+ await new Promise((resolve) => setImmediate(resolve));
60
+ if (!options.expectServer)
61
+ break;
62
+ if (listeners[0]?.listening)
63
+ break;
64
+ }
65
+ return listeners[0];
66
+ };
67
+ beforeEach(() => {
68
+ mocks.isMain = true;
69
+ mocks.connectionThrows = undefined;
70
+ mocks.config = { ...serverConfig(), serverUrl: "https://localhost:0" };
71
+ process.env.NODE_ENV = "test";
72
+ process.env.MONGO_URI = "mongodb://configured:27017/";
73
+ });
74
+ afterEach(async () => {
75
+ vi.restoreAllMocks();
76
+ await Promise.all(mocks.servers.splice(0).map((server) => new Promise((resolve) => {
77
+ server.close(() => resolve());
78
+ })));
79
+ });
80
+ describe("running app.js as the entrypoint", () => {
81
+ test("boots the server", async () => {
82
+ const server = await importApp({ expectServer: true });
83
+ expect(server?.listening).toBe(true);
84
+ });
85
+ test("stays quiet when the module is merely imported", async () => {
86
+ mocks.isMain = false;
87
+ const server = await importApp();
88
+ expect(server).toBeUndefined();
89
+ });
90
+ test("exits when the server cannot start", async () => {
91
+ mocks.connectionThrows = new Error("mongo unreachable");
92
+ const exit = vi
93
+ .spyOn(process, "exit")
94
+ .mockImplementation(() => undefined);
95
+ await importApp();
96
+ expect(exit).toHaveBeenCalledTimes(1);
97
+ expect(exit).toHaveBeenCalledWith(1);
98
+ });
99
+ });
100
+ //# sourceMappingURL=appEntrypoint.unit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appEntrypoint.unit.test.js","sourceRoot":"","sources":["../../src/tests/appEntrypoint.unit.test.ts"],"names":[],"mappings":"AAgBA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAMhD,MAAM,KAAK,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC/B,MAAM,EAAE,IAAI;IACZ,OAAO,EAAE,EAAc;IACvB,gBAAgB,EAAE,SAA8B;IAChD,MAAM,EAAE,EAA+B;CACvC,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE;IACjD,MAAM,MAAM,GAAG,MAAM,cAAc,EAAkC,CAAC;IACtE,OAAO;QACN,GAAG,MAAM;QACT,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM;KAC1B,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,EAAE,CAAC,IAAI,CAAC,wBAAwB,EAAE,GAAG,EAAE,CAAC,CAAC;IACxC,OAAO,EAAE,GAAe,EAAE;QACzB,IAAI,KAAK,CAAC,gBAAgB;YAAE,MAAM,KAAK,CAAC,gBAAgB,CAAC;QACzD,OAAO,EAA2B,CAAC;IACpC,CAAC;CACD,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,sBAAsB,EAAE,GAAG,EAAE,CAAC,CAAC;IACtC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,yBAAyB,CAAC;CACzD,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,qBAAqB,EAAE,GAAG,EAAE,CAAC,CAAC;IACrC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE;CAC/B,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE;IACnD,MAAM,MAAM,GAAG,MAAM,cAAc,EAAoC,CAAC;IACxE,OAAO,EAAE,GAAG,MAAM,EAAE,eAAe,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;AAC3D,CAAC,CAAC,CAAC;AAEH,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE;IACnD,MAAM,MAAM,GAAG,MAAM,cAAc,EAAoC,CAAC;IACxE,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC;AAChD,CAAC,CAAC,CAAC;AAMH,MAAM,SAAS,GAAG,KAAK,EACtB,UAAsC,EAAE,EACV,EAAE;IAChC,EAAE,CAAC,YAAY,EAAE,CAAC;IAClB,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IAC7C,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;IAC/C,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,CAAC,YAAY,CAAC;IACrD,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,kBAAkB,CAC9D,CAAC,GAAG,IAAqC,EAAE,EAAE;QAC5C,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,IAAI,CAAC,CAAC;QACrC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvB,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3B,OAAO,MAAM,CAAC;IACf,CAAC,CACD,CAAC;IAIF,MAAM,kBAAkB,GAAG,WAAW,CAAC,OAAO,CAAC,YAAY,CAAC;IAC5D,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,kBAAkB,CAC/D,CAAC,GAAG,IAA2C,EAAE,EAAE;QAClD,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,IAAI,CAAC,CAAC;QAC3C,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvB,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3B,OAAO,MAAM,CAAC;IACf,CAAC,CACD,CAAC;IACF,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IAI1B,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,GAAG,EAAE,OAAO,EAAE,EAAE,CAAC;QAChD,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;QACtD,IAAI,CAAC,OAAO,CAAC,YAAY;YAAE,MAAM;QACjC,IAAI,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS;YAAE,MAAM;IACpC,CAAC;IACD,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC;AACrB,CAAC,CAAC;AAEF,UAAU,CAAC,GAAG,EAAE;IACf,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,CAAC,gBAAgB,GAAG,SAAS,CAAC;IACnC,KAAK,CAAC,MAAM,GAAG,EAAE,GAAG,YAAY,EAAE,EAAE,SAAS,EAAE,qBAAqB,EAAE,CAAC;IACvE,OAAO,CAAC,GAAG,CAAC,QAAQ,GAAG,MAAM,CAAC;IAC9B,OAAO,CAAC,GAAG,CAAC,SAAS,GAAG,6BAA6B,CAAC;AACvD,CAAC,CAAC,CAAC;AAEH,SAAS,CAAC,KAAK,IAAI,EAAE;IACpB,EAAE,CAAC,eAAe,EAAE,CAAC;IACrB,MAAM,OAAO,CAAC,GAAG,CAChB,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAC1B,CAAC,MAAM,EAAE,EAAE,CACV,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IAC/B,CAAC,CAAC,CACH,CACD,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IACjD,IAAI,CAAC,kBAAkB,EAAE,KAAK,IAAI,EAAE;QACnC,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAGjE,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;QACrB,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;QACrD,KAAK,CAAC,gBAAgB,GAAG,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,EAAE;aACb,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC;aACtB,kBAAkB,CAAC,GAAU,EAAE,CAAC,SAAkB,CAAC,CAAC;QACtD,MAAM,SAAS,EAAE,CAAC;QAClB,MAAM,CAAC,IAAI,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAEtC,MAAM,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=auth.unit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.unit.test.d.ts","sourceRoot":"","sources":["../../src/tests/auth.unit.test.ts"],"names":[],"mappings":""}