@unrift/core 0.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.
Files changed (70) hide show
  1. package/LICENSE +21 -0
  2. package/bin/unrift.mjs +5 -0
  3. package/dist/cjs/index.cjs +31 -0
  4. package/dist/esm/cli.d.ts +3 -0
  5. package/dist/esm/cli.d.ts.map +1 -0
  6. package/dist/esm/cli.js +87 -0
  7. package/dist/esm/context.d.ts +15 -0
  8. package/dist/esm/context.d.ts.map +1 -0
  9. package/dist/esm/context.js +16 -0
  10. package/dist/esm/expect.d.ts +27 -0
  11. package/dist/esm/expect.d.ts.map +1 -0
  12. package/dist/esm/expect.js +43 -0
  13. package/dist/esm/hooks.d.ts +8 -0
  14. package/dist/esm/hooks.d.ts.map +1 -0
  15. package/dist/esm/hooks.js +18 -0
  16. package/dist/esm/index.d.ts +7 -0
  17. package/dist/esm/index.d.ts.map +1 -0
  18. package/dist/esm/index.js +4 -0
  19. package/dist/esm/injected/globals.d.ts +4 -0
  20. package/dist/esm/injected/globals.d.ts.map +1 -0
  21. package/dist/esm/injected/globals.js +6 -0
  22. package/dist/esm/interface.d.ts +18 -0
  23. package/dist/esm/interface.d.ts.map +1 -0
  24. package/dist/esm/interface.js +29 -0
  25. package/dist/esm/matchers.d.ts +2 -0
  26. package/dist/esm/matchers.d.ts.map +1 -0
  27. package/dist/esm/matchers.js +81 -0
  28. package/dist/esm/run.d.ts +21 -0
  29. package/dist/esm/run.d.ts.map +1 -0
  30. package/dist/esm/run.js +57 -0
  31. package/dist/esm/runner.d.ts +14 -0
  32. package/dist/esm/runner.d.ts.map +1 -0
  33. package/dist/esm/runner.js +207 -0
  34. package/dist/esm/suite.d.ts +58 -0
  35. package/dist/esm/suite.d.ts.map +1 -0
  36. package/dist/esm/suite.js +281 -0
  37. package/dist/esm/test.d.ts +18 -0
  38. package/dist/esm/test.d.ts.map +1 -0
  39. package/dist/esm/test.js +52 -0
  40. package/dist/esm/types.d.ts +24 -0
  41. package/dist/esm/types.d.ts.map +1 -0
  42. package/dist/esm/types.js +16 -0
  43. package/dist/esm/utils/colors.d.ts +7 -0
  44. package/dist/esm/utils/colors.d.ts.map +1 -0
  45. package/dist/esm/utils/colors.js +12 -0
  46. package/dist/esm/utils/deepEqual.d.ts +9 -0
  47. package/dist/esm/utils/deepEqual.d.ts.map +1 -0
  48. package/dist/esm/utils/deepEqual.js +201 -0
  49. package/dist/esm/utils/defineConfig.d.ts +12 -0
  50. package/dist/esm/utils/defineConfig.d.ts.map +1 -0
  51. package/dist/esm/utils/defineConfig.js +3 -0
  52. package/dist/esm/utils/discoverTestFiles.d.ts +3 -0
  53. package/dist/esm/utils/discoverTestFiles.d.ts.map +1 -0
  54. package/dist/esm/utils/discoverTestFiles.js +22 -0
  55. package/dist/esm/utils/helpers.d.ts +13 -0
  56. package/dist/esm/utils/helpers.d.ts.map +1 -0
  57. package/dist/esm/utils/helpers.js +29 -0
  58. package/dist/esm/utils/loadConfig.d.ts +9 -0
  59. package/dist/esm/utils/loadConfig.d.ts.map +1 -0
  60. package/dist/esm/utils/loadConfig.js +89 -0
  61. package/dist/esm/utils/looseEqual.d.ts +2 -0
  62. package/dist/esm/utils/looseEqual.d.ts.map +1 -0
  63. package/dist/esm/utils/looseEqual.js +205 -0
  64. package/dist/esm/utils/toThrow.d.ts +11 -0
  65. package/dist/esm/utils/toThrow.d.ts.map +1 -0
  66. package/dist/esm/utils/toThrow.js +93 -0
  67. package/dist/esm/utils/transform.d.ts +10 -0
  68. package/dist/esm/utils/transform.d.ts.map +1 -0
  69. package/dist/esm/utils/transform.js +213 -0
  70. package/package.json +60 -0
@@ -0,0 +1,12 @@
1
+ const enabled = process.stdout.isTTY &&
2
+ process.env.NO_COLOR !== "1" &&
3
+ process.env.NO_COLOR !== "true";
4
+ function wrap(code) {
5
+ return (text) => (enabled ? `\x1b[${code}m${text}\x1b[0m` : text);
6
+ }
7
+ export const colors = {
8
+ green: wrap(32),
9
+ red: wrap(31),
10
+ yellow: wrap(33),
11
+ bold: wrap(1),
12
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Deep structural equality with:
3
+ * - Value semantics for primitives (+ NaN equal, +0/-0 equal)
4
+ * - Structural comparison for Arrays, Maps, Sets, TypedArrays, ArrayBuffer, DataView, Date, RegExp
5
+ * - Prototype-sensitive object comparison
6
+ * - Handles circular references
7
+ */
8
+ export declare function deepEqual<T>(a: T, b: T): boolean;
9
+ //# sourceMappingURL=deepEqual.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deepEqual.d.ts","sourceRoot":"","sources":["../../../src/utils/deepEqual.ts"],"names":[],"mappings":"AA+OA;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,CAShD"}
@@ -0,0 +1,201 @@
1
+ import { isObjectLike, isPrimitive, isTypedArray, sameValue, } from "./helpers.js";
2
+ function deepEqualInternal(left, right, leftToRightSeen) {
3
+ const worklist = [[left, right]];
4
+ while (worklist.length) {
5
+ const [currentLeft, currentRight] = worklist.pop();
6
+ if (sameValue(currentLeft, currentRight))
7
+ continue;
8
+ // Types must match at this point
9
+ if (typeof currentLeft !== typeof currentRight)
10
+ return false;
11
+ // Primitive mismatch - we already handled equality above
12
+ if (isPrimitive(currentLeft) || isPrimitive(currentRight))
13
+ return false;
14
+ // Functions, etc. – only ever equal by reference, which we've already checked
15
+ if (!isObjectLike(currentLeft) || !isObjectLike(currentRight))
16
+ return false;
17
+ const leftObj = currentLeft;
18
+ const rightObj = currentRight;
19
+ // Cycle detection: require consistent mapping for left-side objects
20
+ const previouslyMappedRight = leftToRightSeen.get(leftObj);
21
+ if (previouslyMappedRight !== undefined) {
22
+ if (previouslyMappedRight !== rightObj)
23
+ return false;
24
+ continue;
25
+ }
26
+ leftToRightSeen.set(leftObj, rightObj);
27
+ // Arrays - prototype-sensitive handled by prototype check later
28
+ const leftIsArray = Array.isArray(currentLeft);
29
+ const rightIsArray = Array.isArray(currentRight);
30
+ if (leftIsArray || rightIsArray) {
31
+ if (!leftIsArray || !rightIsArray)
32
+ return false;
33
+ const leftArr = currentLeft;
34
+ const rightArr = currentRight;
35
+ if (leftArr.length !== rightArr.length)
36
+ return false;
37
+ // Preserve sparseness by reading indexed values directly.
38
+ for (let index = 0; index < leftArr.length; index++) {
39
+ worklist.push([leftArr[index], rightArr[index]]);
40
+ }
41
+ continue;
42
+ }
43
+ // Typed arrays
44
+ const leftIsTyped = isTypedArray(currentLeft);
45
+ const rightIsTyped = isTypedArray(currentRight);
46
+ if (leftIsTyped || rightIsTyped) {
47
+ if (!leftIsTyped || !rightIsTyped)
48
+ return false;
49
+ const leftTA = currentLeft;
50
+ const rightTA = currentRight;
51
+ if (leftTA.constructor !== rightTA.constructor)
52
+ return false;
53
+ if (leftTA.length !== rightTA.length)
54
+ return false;
55
+ for (let index = 0; index < leftTA.length; index++) {
56
+ if (!sameValue(leftTA[index], rightTA[index]))
57
+ return false;
58
+ }
59
+ continue;
60
+ }
61
+ // ArrayBuffer
62
+ const leftIsBuffer = currentLeft instanceof ArrayBuffer;
63
+ const rightIsBuffer = currentRight instanceof ArrayBuffer;
64
+ if (leftIsBuffer || rightIsBuffer) {
65
+ if (!leftIsBuffer || !rightIsBuffer)
66
+ return false;
67
+ const leftBuf = currentLeft;
68
+ const rightBuf = currentRight;
69
+ if (leftBuf.byteLength !== rightBuf.byteLength)
70
+ return false;
71
+ const leftBytes = new Uint8Array(leftBuf);
72
+ const rightBytes = new Uint8Array(rightBuf);
73
+ for (let index = 0; index < leftBytes.length; index++) {
74
+ if (leftBytes[index] !== rightBytes[index])
75
+ return false;
76
+ }
77
+ continue;
78
+ }
79
+ // DataView
80
+ const leftIsView = currentLeft instanceof DataView;
81
+ const rightIsView = currentRight instanceof DataView;
82
+ if (leftIsView || rightIsView) {
83
+ if (!leftIsView || !rightIsView)
84
+ return false;
85
+ const leftView = currentLeft;
86
+ const rightView = currentRight;
87
+ if (leftView.byteLength !== rightView.byteLength)
88
+ return false;
89
+ for (let offset = 0; offset < leftView.byteLength; offset++) {
90
+ if (leftView.getUint8(offset) !== rightView.getUint8(offset))
91
+ return false;
92
+ }
93
+ continue;
94
+ }
95
+ // Date
96
+ const leftIsDate = currentLeft instanceof Date;
97
+ const rightIsDate = currentRight instanceof Date;
98
+ if (leftIsDate || rightIsDate) {
99
+ if (!leftIsDate || !rightIsDate)
100
+ return false;
101
+ if (currentLeft.getTime() !== currentRight.getTime())
102
+ return false;
103
+ continue;
104
+ }
105
+ // RegExp
106
+ const leftIsRegExp = currentLeft instanceof RegExp;
107
+ const rightIsRegExp = currentRight instanceof RegExp;
108
+ if (leftIsRegExp || rightIsRegExp) {
109
+ if (!leftIsRegExp || !rightIsRegExp)
110
+ return false;
111
+ const leftRe = currentLeft;
112
+ const rightRe = currentRight;
113
+ if (leftRe.source !== rightRe.source)
114
+ return false;
115
+ if (leftRe.flags !== rightRe.flags)
116
+ return false;
117
+ continue;
118
+ }
119
+ // Set
120
+ const leftIsSet = currentLeft instanceof Set;
121
+ const rightIsSet = currentRight instanceof Set;
122
+ if (leftIsSet || rightIsSet) {
123
+ if (!leftIsSet || !rightIsSet)
124
+ return false;
125
+ const leftSet = currentLeft;
126
+ const rightSet = currentRight;
127
+ if (leftSet.size !== rightSet.size)
128
+ return false;
129
+ const unmatchedRightValues = new Set(rightSet);
130
+ outer: for (const leftValue of leftSet) {
131
+ for (const rightValue of unmatchedRightValues) {
132
+ if (deepEqualInternal(leftValue, rightValue, leftToRightSeen)) {
133
+ unmatchedRightValues.delete(rightValue);
134
+ continue outer;
135
+ }
136
+ }
137
+ return false;
138
+ }
139
+ continue;
140
+ }
141
+ // Map
142
+ const leftIsMap = currentLeft instanceof Map;
143
+ const rightIsMap = currentRight instanceof Map;
144
+ if (leftIsMap || rightIsMap) {
145
+ if (!leftIsMap || !rightIsMap)
146
+ return false;
147
+ const leftMap = currentLeft;
148
+ const rightMap = currentRight;
149
+ if (leftMap.size !== rightMap.size)
150
+ return false;
151
+ const unmatchedRightEntries = new Set(rightMap.entries());
152
+ outer: for (const [leftKey, leftValue] of leftMap.entries()) {
153
+ for (const candidate of unmatchedRightEntries) {
154
+ const [rightKey, rightValue] = candidate;
155
+ if (deepEqualInternal(leftKey, rightKey, leftToRightSeen) &&
156
+ deepEqualInternal(leftValue, rightValue, leftToRightSeen)) {
157
+ unmatchedRightEntries.delete(candidate);
158
+ continue outer;
159
+ }
160
+ }
161
+ return false;
162
+ }
163
+ continue;
164
+ }
165
+ // Fallback: plain objects / custom class instances (prototype-sensitive)
166
+ const leftProto = Object.getPrototypeOf(currentLeft);
167
+ const rightProto = Object.getPrototypeOf(currentRight);
168
+ if (leftProto !== rightProto)
169
+ return false;
170
+ const leftKeys = Object.keys(currentLeft);
171
+ const rightKeys = Object.keys(currentRight);
172
+ if (leftKeys.length !== rightKeys.length)
173
+ return false;
174
+ const leftRecord = currentLeft;
175
+ const rightRecord = currentRight;
176
+ for (let i = 0; i < leftKeys.length; i++) {
177
+ const key = leftKeys[i];
178
+ if (!Object.hasOwn(rightRecord, key))
179
+ return false;
180
+ worklist.push([leftRecord[key], rightRecord[key]]);
181
+ }
182
+ }
183
+ return true;
184
+ }
185
+ /**
186
+ * Deep structural equality with:
187
+ * - Value semantics for primitives (+ NaN equal, +0/-0 equal)
188
+ * - Structural comparison for Arrays, Maps, Sets, TypedArrays, ArrayBuffer, DataView, Date, RegExp
189
+ * - Prototype-sensitive object comparison
190
+ * - Handles circular references
191
+ */
192
+ export function deepEqual(a, b) {
193
+ if (sameValue(a, b))
194
+ return true;
195
+ // Fast-path for primitive inequality
196
+ if (isPrimitive(a) || isPrimitive(b))
197
+ return false;
198
+ if (!isObjectLike(a) || !isObjectLike(b))
199
+ return false;
200
+ return deepEqualInternal(a, b, new WeakMap());
201
+ }
@@ -0,0 +1,12 @@
1
+ export interface UnriftConfigOptions {
2
+ testDir?: string;
3
+ rootDir?: string;
4
+ timeoutMs?: number;
5
+ bail?: boolean;
6
+ pattern?: string;
7
+ includes?: string[];
8
+ excludes?: string[];
9
+ matchers?: string[];
10
+ }
11
+ export declare function defineConfig(options: UnriftConfigOptions): UnriftConfigOptions;
12
+ //# sourceMappingURL=defineConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defineConfig.d.ts","sourceRoot":"","sources":["../../../src/utils/defineConfig.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,wBAAgB,YAAY,CAC1B,OAAO,EAAE,mBAAmB,GAC3B,mBAAmB,CAErB"}
@@ -0,0 +1,3 @@
1
+ export function defineConfig(options) {
2
+ return options;
3
+ }
@@ -0,0 +1,3 @@
1
+ export declare function isTestFileName(name: string): boolean;
2
+ export declare function discoverTestFiles(dir: string): string[];
3
+ //# sourceMappingURL=discoverTestFiles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discoverTestFiles.d.ts","sourceRoot":"","sources":["../../../src/utils/discoverTestFiles.ts"],"names":[],"mappings":"AAGA,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAqBvD"}
@@ -0,0 +1,22 @@
1
+ import { existsSync, readdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ export function isTestFileName(name) {
4
+ return /\.(spec|test)\.(ts|js)$/.test(name);
5
+ }
6
+ export function discoverTestFiles(dir) {
7
+ if (!existsSync(dir))
8
+ return [];
9
+ const entries = readdirSync(dir, { withFileTypes: true });
10
+ const files = [];
11
+ for (const entry of entries) {
12
+ const fullPath = join(dir, entry.name);
13
+ if (entry.isDirectory()) {
14
+ files.push(...discoverTestFiles(fullPath));
15
+ continue;
16
+ }
17
+ if (entry.isFile() && isTestFileName(entry.name)) {
18
+ files.push(fullPath);
19
+ }
20
+ }
21
+ return files;
22
+ }
@@ -0,0 +1,13 @@
1
+ type Primitive = null | undefined | string | number | boolean | symbol | bigint;
2
+ export type TypedArray = Uint8Array | Int8Array | Uint16Array | Int16Array | Uint32Array | Int32Array | Float32Array | Float64Array | BigInt64Array | BigUint64Array;
3
+ export declare function isPrimitive(value: unknown): value is Primitive;
4
+ export declare function isObjectLike(value: unknown): value is object;
5
+ export declare function isTypedArray(value: unknown): value is TypedArray;
6
+ /**
7
+ * SameValueZero-ish semantics:
8
+ * - NaN equals NaN
9
+ * - +0 and -0 are equal
10
+ */
11
+ export declare function sameValue(a: unknown, b: unknown): boolean;
12
+ export {};
13
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/helpers.ts"],"names":[],"mappings":"AAAA,KAAK,SAAS,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAEhF,MAAM,MAAM,UAAU,GAClB,UAAU,GACV,SAAS,GACT,WAAW,GACX,UAAU,GACV,WAAW,GACX,UAAU,GACV,YAAY,GACZ,YAAY,GACZ,aAAa,GACb,cAAc,CAAC;AAEnB,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAY9D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAE5D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAEhE;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CASzD"}
@@ -0,0 +1,29 @@
1
+ export function isPrimitive(value) {
2
+ const t = typeof value;
3
+ return (value === null ||
4
+ t === "string" ||
5
+ t === "number" ||
6
+ t === "boolean" ||
7
+ t === "undefined" ||
8
+ t === "symbol" ||
9
+ t === "bigint");
10
+ }
11
+ export function isObjectLike(value) {
12
+ return typeof value === "object" && value !== null;
13
+ }
14
+ export function isTypedArray(value) {
15
+ return ArrayBuffer.isView(value) && !(value instanceof DataView);
16
+ }
17
+ /**
18
+ * SameValueZero-ish semantics:
19
+ * - NaN equals NaN
20
+ * - +0 and -0 are equal
21
+ */
22
+ export function sameValue(a, b) {
23
+ if (a === b)
24
+ return true;
25
+ return (typeof a === "number" &&
26
+ typeof b === "number" &&
27
+ Number.isNaN(a) &&
28
+ Number.isNaN(b));
29
+ }
@@ -0,0 +1,9 @@
1
+ import type { UnriftConfigOptions } from "./defineConfig";
2
+ export type LoadedConfig = {
3
+ config: UnriftConfigOptions;
4
+ configPath: string;
5
+ configDir: string;
6
+ };
7
+ export declare function loadConfigFromPath(configPath: string): Promise<LoadedConfig>;
8
+ export declare function loadConfig(startDir: string): Promise<LoadedConfig | null>;
9
+ //# sourceMappingURL=loadConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadConfig.d.ts","sourceRoot":"","sources":["../../../src/utils/loadConfig.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAU1D,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,mBAAmB,CAAC;IAE5B,UAAU,EAAE,MAAM,CAAC;IAEnB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAuCF,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,CAAC,CAevB;AAqCD,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAc9B"}
@@ -0,0 +1,89 @@
1
+ import { toImportUrl } from "./transform.js";
2
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
3
+ import { dirname, isAbsolute, join, resolve } from "node:path";
4
+ /**
5
+ * Regex to match valid Unrift config file names.
6
+ * Matches:
7
+ * - unrift.config.ts/js/mjs/cjs/json
8
+ * - unrift.<anything>.config.ts/js/mjs/cjs/json (e.g. unrift.only.config.mjs)
9
+ */
10
+ const CONFIG_RE = /^unrift(?:\.[^.]+)*\.config\.(ts|js|mjs|cjs|json)$/;
11
+ function isConfigFileName(name) {
12
+ return CONFIG_RE.test(name);
13
+ }
14
+ async function importConfigFile(configPath) {
15
+ if (configPath.endsWith(".json")) {
16
+ try {
17
+ const raw = readFileSync(configPath, "utf8");
18
+ return JSON.parse(raw);
19
+ }
20
+ catch (err) {
21
+ const msg = err instanceof Error ? err.message : String(err);
22
+ throw new Error(`Failed to parse JSON config at ${configPath}: ${msg}`);
23
+ }
24
+ }
25
+ const module = await import(toImportUrl(configPath, "bundle-config"));
26
+ return module.default ?? module.config;
27
+ }
28
+ function assertConfigObject(config, absolutePath) {
29
+ if (!config || typeof config !== "object") {
30
+ throw new Error(`Unrift config at ${absolutePath} must export an object (default export recommended).`);
31
+ }
32
+ }
33
+ function toAbsolutePath(p) {
34
+ return isAbsolute(p) ? p : resolve(process.cwd(), p);
35
+ }
36
+ export async function loadConfigFromPath(configPath) {
37
+ const absolutePath = toAbsolutePath(configPath);
38
+ if (!existsSync(absolutePath)) {
39
+ throw new Error(`Config file not found: ${absolutePath}`);
40
+ }
41
+ const loaded = await importConfigFile(absolutePath);
42
+ assertConfigObject(loaded, absolutePath);
43
+ return {
44
+ config: loaded,
45
+ configPath: absolutePath,
46
+ configDir: dirname(absolutePath),
47
+ };
48
+ }
49
+ function findConfigPath(startDir) {
50
+ let dir = resolve(startDir);
51
+ while (true) {
52
+ let entries = [];
53
+ try {
54
+ entries = readdirSync(dir);
55
+ }
56
+ catch {
57
+ // ignore unreadable dirs
58
+ }
59
+ // Prefer the "default" config if multiple exist in same folder
60
+ const candidates = entries.filter(isConfigFileName).sort((a, b) => {
61
+ const aIsDefault = a.startsWith("unrift.config.");
62
+ const bIsDefault = b.startsWith("unrift.config.");
63
+ if (aIsDefault !== bIsDefault)
64
+ return aIsDefault ? -1 : 1;
65
+ return a.localeCompare(b);
66
+ });
67
+ if (candidates.length > 0) {
68
+ return join(dir, candidates[0]);
69
+ }
70
+ const parent = dirname(dir);
71
+ if (parent === dir)
72
+ break;
73
+ dir = parent;
74
+ }
75
+ return null;
76
+ }
77
+ export async function loadConfig(startDir) {
78
+ const found = findConfigPath(startDir);
79
+ if (!found)
80
+ return null;
81
+ const absolutePath = toAbsolutePath(found);
82
+ const loaded = await importConfigFile(absolutePath);
83
+ assertConfigObject(loaded, absolutePath);
84
+ return {
85
+ config: loaded,
86
+ configPath: absolutePath,
87
+ configDir: dirname(absolutePath),
88
+ };
89
+ }
@@ -0,0 +1,2 @@
1
+ export declare function looseEqual<T>(left: T, right: T): boolean;
2
+ //# sourceMappingURL=looseEqual.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"looseEqual.d.ts","sourceRoot":"","sources":["../../../src/utils/looseEqual.ts"],"names":[],"mappings":"AAuRA,wBAAgB,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAWxD"}
@@ -0,0 +1,205 @@
1
+ import { isObjectLike, isPrimitive, isTypedArray, sameValue, } from "./helpers.js";
2
+ function looseEqualInternal(left, right, leftToRightSeen, rightToLeftSeen) {
3
+ const worklist = [[left, right]];
4
+ while (worklist.length) {
5
+ const [currentLeft, currentRight] = worklist.pop();
6
+ if (sameValue(currentLeft, currentRight))
7
+ continue;
8
+ if (typeof currentLeft !== typeof currentRight)
9
+ return false;
10
+ if (isPrimitive(currentLeft) || isPrimitive(currentRight))
11
+ return false;
12
+ if (!isObjectLike(currentLeft) || !isObjectLike(currentRight))
13
+ return false;
14
+ const leftObj = currentLeft;
15
+ const rightObj = currentRight;
16
+ // Cycle / aliasing consistency (bijection-ish)
17
+ const mappedRight = leftToRightSeen.get(leftObj);
18
+ if (mappedRight !== undefined) {
19
+ if (mappedRight !== rightObj)
20
+ return false;
21
+ continue;
22
+ }
23
+ const mappedLeft = rightToLeftSeen.get(rightObj);
24
+ if (mappedLeft !== undefined) {
25
+ if (mappedLeft !== leftObj)
26
+ return false;
27
+ continue;
28
+ }
29
+ leftToRightSeen.set(leftObj, rightObj);
30
+ rightToLeftSeen.set(rightObj, leftObj);
31
+ // Arrays: sparse holes treated as undefined
32
+ const leftIsArray = Array.isArray(currentLeft);
33
+ const rightIsArray = Array.isArray(currentRight);
34
+ if (leftIsArray || rightIsArray) {
35
+ if (!leftIsArray || !rightIsArray)
36
+ return false;
37
+ const leftArr = currentLeft;
38
+ const rightArr = currentRight;
39
+ if (leftArr.length !== rightArr.length)
40
+ return false;
41
+ for (let index = 0; index < leftArr.length; index++) {
42
+ const leftValue = Object.hasOwn(leftArr, index)
43
+ ? leftArr[index]
44
+ : undefined;
45
+ const rightValue = Object.hasOwn(rightArr, index)
46
+ ? rightArr[index]
47
+ : undefined;
48
+ worklist.push([leftValue, rightValue]);
49
+ }
50
+ continue;
51
+ }
52
+ // Typed arrays
53
+ const leftIsTypedArray = isTypedArray(currentLeft);
54
+ const rightIsTypedArray = isTypedArray(currentRight);
55
+ if (leftIsTypedArray || rightIsTypedArray) {
56
+ if (!leftIsTypedArray || !rightIsTypedArray)
57
+ return false;
58
+ const leftTypedArray = currentLeft;
59
+ const rightTypedArray = currentRight;
60
+ if (leftTypedArray.constructor !== rightTypedArray.constructor)
61
+ return false;
62
+ if (leftTypedArray.length !== rightTypedArray.length)
63
+ return false;
64
+ for (let index = 0; index < leftTypedArray.length; index++) {
65
+ if (!sameValue(leftTypedArray[index], rightTypedArray[index]))
66
+ return false;
67
+ }
68
+ continue;
69
+ }
70
+ // ArrayBuffer
71
+ const leftIsBuffer = currentLeft instanceof ArrayBuffer;
72
+ const rightIsBuffer = currentRight instanceof ArrayBuffer;
73
+ if (leftIsBuffer || rightIsBuffer) {
74
+ if (!leftIsBuffer || !rightIsBuffer)
75
+ return false;
76
+ const leftBuffer = currentLeft;
77
+ const rightBuffer = currentRight;
78
+ if (leftBuffer.byteLength !== rightBuffer.byteLength)
79
+ return false;
80
+ const leftBytes = new Uint8Array(leftBuffer);
81
+ const rightBytes = new Uint8Array(rightBuffer);
82
+ for (let index = 0; index < leftBytes.length; index++) {
83
+ if (leftBytes[index] !== rightBytes[index])
84
+ return false;
85
+ }
86
+ continue;
87
+ }
88
+ // DataView
89
+ const leftIsDataView = currentLeft instanceof DataView;
90
+ const rightIsDataView = currentRight instanceof DataView;
91
+ if (leftIsDataView || rightIsDataView) {
92
+ if (!leftIsDataView || !rightIsDataView)
93
+ return false;
94
+ const leftView = currentLeft;
95
+ const rightView = currentRight;
96
+ if (leftView.byteLength !== rightView.byteLength)
97
+ return false;
98
+ for (let offset = 0; offset < leftView.byteLength; offset++) {
99
+ if (leftView.getUint8(offset) !== rightView.getUint8(offset))
100
+ return false;
101
+ }
102
+ continue;
103
+ }
104
+ // Date
105
+ const leftIsDate = currentLeft instanceof Date;
106
+ const rightIsDate = currentRight instanceof Date;
107
+ if (leftIsDate || rightIsDate) {
108
+ if (!leftIsDate || !rightIsDate)
109
+ return false;
110
+ if (currentLeft.getTime() !== currentRight.getTime()) {
111
+ return false;
112
+ }
113
+ continue;
114
+ }
115
+ // RegExp
116
+ const leftIsRegExp = currentLeft instanceof RegExp;
117
+ const rightIsRegExp = currentRight instanceof RegExp;
118
+ if (leftIsRegExp || rightIsRegExp) {
119
+ if (!leftIsRegExp || !rightIsRegExp)
120
+ return false;
121
+ const leftRe = currentLeft;
122
+ const rightRe = currentRight;
123
+ if (leftRe.source !== rightRe.source)
124
+ return false;
125
+ if (leftRe.flags !== rightRe.flags)
126
+ return false;
127
+ continue;
128
+ }
129
+ // Set (order-insensitive)
130
+ const leftIsSet = currentLeft instanceof Set;
131
+ const rightIsSet = currentRight instanceof Set;
132
+ if (leftIsSet || rightIsSet) {
133
+ if (!leftIsSet || !rightIsSet)
134
+ return false;
135
+ const leftSet = currentLeft;
136
+ const rightSet = currentRight;
137
+ if (leftSet.size !== rightSet.size)
138
+ return false;
139
+ const remainingRight = new Set(rightSet);
140
+ outer: for (const leftValue of leftSet) {
141
+ for (const rightValue of remainingRight) {
142
+ if (looseEqualInternal(leftValue, rightValue, leftToRightSeen, rightToLeftSeen)) {
143
+ remainingRight.delete(rightValue);
144
+ continue outer;
145
+ }
146
+ }
147
+ return false;
148
+ }
149
+ continue;
150
+ }
151
+ // Map (order-insensitive by entries; keys compared via looseEqual)
152
+ const leftIsMap = currentLeft instanceof Map;
153
+ const rightIsMap = currentRight instanceof Map;
154
+ if (leftIsMap || rightIsMap) {
155
+ if (!leftIsMap || !rightIsMap)
156
+ return false;
157
+ const leftMap = currentLeft;
158
+ const rightMap = currentRight;
159
+ if (leftMap.size !== rightMap.size)
160
+ return false;
161
+ const remainingRightEntries = new Set(rightMap.entries());
162
+ outer: for (const [leftKey, leftValue] of leftMap.entries()) {
163
+ for (const candidate of remainingRightEntries) {
164
+ const [rightKey, rightValue] = candidate;
165
+ if (looseEqualInternal(leftKey, rightKey, leftToRightSeen, rightToLeftSeen) &&
166
+ looseEqualInternal(leftValue, rightValue, leftToRightSeen, rightToLeftSeen)) {
167
+ remainingRightEntries.delete(candidate);
168
+ continue outer;
169
+ }
170
+ }
171
+ return false;
172
+ }
173
+ continue;
174
+ }
175
+ // Fallback: compare by enumerable shape (prototype-insensitive).
176
+ // Missing ≈ undefined via union-of-keys comparison.
177
+ const leftKeys = Object.keys(currentLeft);
178
+ const rightKeys = Object.keys(currentRight);
179
+ const allKeys = new Set([...leftKeys, ...rightKeys]);
180
+ const leftRecord = currentLeft;
181
+ const rightRecord = currentRight;
182
+ for (const key of allKeys) {
183
+ const leftHasKey = Object.hasOwn(leftRecord, key);
184
+ const rightHasKey = Object.hasOwn(rightRecord, key);
185
+ const leftValue = leftHasKey ? leftRecord[key] : undefined;
186
+ const rightValue = rightHasKey ? rightRecord[key] : undefined;
187
+ // If one side is missing and the other side is present-but-not-undefined, fail.
188
+ if (leftHasKey !== rightHasKey) {
189
+ if (leftValue !== undefined || rightValue !== undefined)
190
+ return false;
191
+ }
192
+ worklist.push([leftValue, rightValue]);
193
+ }
194
+ }
195
+ return true;
196
+ }
197
+ export function looseEqual(left, right) {
198
+ if (sameValue(left, right))
199
+ return true;
200
+ if (isPrimitive(left) || isPrimitive(right))
201
+ return false;
202
+ if (!isObjectLike(left) || !isObjectLike(right))
203
+ return false;
204
+ return looseEqualInternal(left, right, new WeakMap(), new WeakMap());
205
+ }
@@ -0,0 +1,11 @@
1
+ export type ToThrowExpected = undefined | string | RegExp | (new (...args: unknown[]) => Error) | {
2
+ message?: string | RegExp;
3
+ name?: string;
4
+ };
5
+ interface MatchResult {
6
+ pass: boolean;
7
+ message: () => string;
8
+ }
9
+ export declare function toThrow(received: unknown, expected?: ToThrowExpected, isNot?: boolean): MatchResult;
10
+ export {};
11
+ //# sourceMappingURL=toThrow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toThrow.d.ts","sourceRoot":"","sources":["../../../src/utils/toThrow.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,MAAM,GACN,MAAM,GACN,CAAC,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,KAAK,CAAC,GACnC;IAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD,UAAU,WAAW;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,MAAM,CAAC;CACvB;AAgED,wBAAgB,OAAO,CACrB,QAAQ,EAAE,OAAO,EACjB,QAAQ,CAAC,EAAE,eAAe,EAC1B,KAAK,UAAQ,GACZ,WAAW,CAgDb"}