@liase/core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +62 -0
  3. package/dist/ActionContext.d.ts +603 -0
  4. package/dist/ActionContext.d.ts.map +1 -0
  5. package/dist/ActionContext.js +227 -0
  6. package/dist/DomSelection.d.ts +30 -0
  7. package/dist/DomSelection.d.ts.map +1 -0
  8. package/dist/DomSelection.js +60 -0
  9. package/dist/Liase.d.ts +40 -0
  10. package/dist/Liase.d.ts.map +1 -0
  11. package/dist/Liase.js +105 -0
  12. package/dist/LiaseQuery.d.ts +593 -0
  13. package/dist/LiaseQuery.d.ts.map +1 -0
  14. package/dist/LiaseQuery.js +145 -0
  15. package/dist/actionHelpers.d.ts +30 -0
  16. package/dist/actionHelpers.d.ts.map +1 -0
  17. package/dist/actionHelpers.js +73 -0
  18. package/dist/constructorExecution.d.ts +7 -0
  19. package/dist/constructorExecution.d.ts.map +1 -0
  20. package/dist/constructorExecution.js +149 -0
  21. package/dist/generateResponse.d.ts +14 -0
  22. package/dist/generateResponse.d.ts.map +1 -0
  23. package/dist/generateResponse.js +88 -0
  24. package/dist/index.d.ts +13 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +6 -0
  27. package/dist/lib/fetch.d.ts +10 -0
  28. package/dist/lib/fetch.d.ts.map +1 -0
  29. package/dist/lib/fetch.js +68 -0
  30. package/dist/lib/hooks.d.ts +7 -0
  31. package/dist/lib/hooks.d.ts.map +1 -0
  32. package/dist/lib/hooks.js +13 -0
  33. package/dist/lib/networkRequestsCache.d.ts +23 -0
  34. package/dist/lib/networkRequestsCache.d.ts.map +1 -0
  35. package/dist/lib/networkRequestsCache.js +81 -0
  36. package/dist/lib/networkRequestsHistory.d.ts +21 -0
  37. package/dist/lib/networkRequestsHistory.d.ts.map +1 -0
  38. package/dist/lib/networkRequestsHistory.js +119 -0
  39. package/dist/lib/time.d.ts +2 -0
  40. package/dist/lib/time.d.ts.map +1 -0
  41. package/dist/lib/time.js +31 -0
  42. package/dist/lib/utils.d.ts +46 -0
  43. package/dist/lib/utils.d.ts.map +1 -0
  44. package/dist/lib/utils.js +208 -0
  45. package/dist/lib/zod.d.ts +35 -0
  46. package/dist/lib/zod.d.ts.map +1 -0
  47. package/dist/lib/zod.js +290 -0
  48. package/dist/loadUrl.d.ts +45 -0
  49. package/dist/loadUrl.d.ts.map +1 -0
  50. package/dist/loadUrl.js +111 -0
  51. package/dist/plugins/built-in-sources/bluesky/client.d.ts +11 -0
  52. package/dist/plugins/built-in-sources/bluesky/client.d.ts.map +1 -0
  53. package/dist/plugins/built-in-sources/bluesky/client.js +17 -0
  54. package/dist/plugins/built-in-sources/bluesky/index.d.ts +1115 -0
  55. package/dist/plugins/built-in-sources/bluesky/index.d.ts.map +1 -0
  56. package/dist/plugins/built-in-sources/bluesky/index.js +14 -0
  57. package/dist/plugins/built-in-sources/bluesky/requestHandlers/feed.d.ts +378 -0
  58. package/dist/plugins/built-in-sources/bluesky/requestHandlers/feed.d.ts.map +1 -0
  59. package/dist/plugins/built-in-sources/bluesky/requestHandlers/feed.js +77 -0
  60. package/dist/plugins/built-in-sources/bluesky/requestHandlers/mediaSearch.d.ts +390 -0
  61. package/dist/plugins/built-in-sources/bluesky/requestHandlers/mediaSearch.d.ts.map +1 -0
  62. package/dist/plugins/built-in-sources/bluesky/requestHandlers/mediaSearch.js +87 -0
  63. package/dist/plugins/built-in-sources/bluesky/requestHandlers/singleMedia.d.ts +351 -0
  64. package/dist/plugins/built-in-sources/bluesky/requestHandlers/singleMedia.d.ts.map +1 -0
  65. package/dist/plugins/built-in-sources/bluesky/requestHandlers/singleMedia.js +56 -0
  66. package/dist/plugins/built-in-sources/bluesky/shared.d.ts +53 -0
  67. package/dist/plugins/built-in-sources/bluesky/shared.d.ts.map +1 -0
  68. package/dist/plugins/built-in-sources/bluesky/shared.js +127 -0
  69. package/dist/plugins/built-in-sources/bluesky/types.d.ts +488 -0
  70. package/dist/plugins/built-in-sources/bluesky/types.d.ts.map +1 -0
  71. package/dist/plugins/built-in-sources/bluesky/types.js +48 -0
  72. package/dist/plugins/built-in-sources/giphy/index.d.ts +584 -0
  73. package/dist/plugins/built-in-sources/giphy/index.d.ts.map +1 -0
  74. package/dist/plugins/built-in-sources/giphy/index.js +9 -0
  75. package/dist/plugins/built-in-sources/giphy/requestHandlers/mediaSearch.d.ts +310 -0
  76. package/dist/plugins/built-in-sources/giphy/requestHandlers/mediaSearch.d.ts.map +1 -0
  77. package/dist/plugins/built-in-sources/giphy/requestHandlers/mediaSearch.js +71 -0
  78. package/dist/plugins/built-in-sources/giphy/requestHandlers/singleMedia.d.ts +274 -0
  79. package/dist/plugins/built-in-sources/giphy/requestHandlers/singleMedia.d.ts.map +1 -0
  80. package/dist/plugins/built-in-sources/giphy/requestHandlers/singleMedia.js +43 -0
  81. package/dist/plugins/built-in-sources/giphy/shared.d.ts +24 -0
  82. package/dist/plugins/built-in-sources/giphy/shared.d.ts.map +1 -0
  83. package/dist/plugins/built-in-sources/giphy/shared.js +30 -0
  84. package/dist/plugins/built-in-sources/giphy/types.d.ts +398 -0
  85. package/dist/plugins/built-in-sources/giphy/types.d.ts.map +1 -0
  86. package/dist/plugins/built-in-sources/giphy/types.js +46 -0
  87. package/dist/plugins/built-in-sources/index.d.ts +1698 -0
  88. package/dist/plugins/built-in-sources/index.d.ts.map +1 -0
  89. package/dist/plugins/built-in-sources/index.js +5 -0
  90. package/dist/schemas/constructor.d.ts +14 -0
  91. package/dist/schemas/constructor.d.ts.map +1 -0
  92. package/dist/schemas/constructor.js +33 -0
  93. package/dist/schemas/file.d.ts +124 -0
  94. package/dist/schemas/file.d.ts.map +1 -0
  95. package/dist/schemas/file.js +28 -0
  96. package/dist/schemas/finderOptions.d.ts +389 -0
  97. package/dist/schemas/finderOptions.d.ts.map +1 -0
  98. package/dist/schemas/finderOptions.js +7 -0
  99. package/dist/schemas/media.d.ts +433 -0
  100. package/dist/schemas/media.d.ts.map +1 -0
  101. package/dist/schemas/media.js +81 -0
  102. package/dist/schemas/plugin.d.ts +298 -0
  103. package/dist/schemas/plugin.d.ts.map +1 -0
  104. package/dist/schemas/plugin.js +19 -0
  105. package/dist/schemas/primitives.d.ts +4 -0
  106. package/dist/schemas/primitives.d.ts.map +1 -0
  107. package/dist/schemas/primitives.js +11 -0
  108. package/dist/schemas/queryOptions.d.ts +19 -0
  109. package/dist/schemas/queryOptions.d.ts.map +1 -0
  110. package/dist/schemas/queryOptions.js +9 -0
  111. package/dist/schemas/request.d.ts +77 -0
  112. package/dist/schemas/request.d.ts.map +1 -0
  113. package/dist/schemas/request.js +21 -0
  114. package/dist/schemas/requestHandler.d.ts +126 -0
  115. package/dist/schemas/requestHandler.d.ts.map +1 -0
  116. package/dist/schemas/requestHandler.js +26 -0
  117. package/dist/schemas/response.d.ts +4673 -0
  118. package/dist/schemas/response.d.ts.map +1 -0
  119. package/dist/schemas/response.js +51 -0
  120. package/dist/schemas/secrets.d.ts +4 -0
  121. package/dist/schemas/secrets.d.ts.map +1 -0
  122. package/dist/schemas/secrets.js +2 -0
  123. package/dist/schemas/source.d.ts +202 -0
  124. package/dist/schemas/source.d.ts.map +1 -0
  125. package/dist/schemas/source.js +10 -0
  126. package/dist/test/fixtures/currentTimeSource.d.ts +53 -0
  127. package/dist/test/fixtures/currentTimeSource.d.ts.map +1 -0
  128. package/dist/test/fixtures/currentTimeSource.js +76 -0
  129. package/dist/test/fixtures/examplePlugin.d.ts +266 -0
  130. package/dist/test/fixtures/examplePlugin.d.ts.map +1 -0
  131. package/dist/test/fixtures/examplePlugin.js +4 -0
  132. package/dist/test/fixtures/exampleSource.d.ts +265 -0
  133. package/dist/test/fixtures/exampleSource.d.ts.map +1 -0
  134. package/dist/test/fixtures/exampleSource.js +107 -0
  135. package/dist/test/testFiles/internal/caching.test.d.ts +2 -0
  136. package/dist/test/testFiles/internal/caching.test.d.ts.map +1 -0
  137. package/dist/test/testFiles/internal/caching.test.js +116 -0
  138. package/dist/test/testFiles/internal/mediaTypeGuessing.test.d.ts +2 -0
  139. package/dist/test/testFiles/internal/mediaTypeGuessing.test.d.ts.map +1 -0
  140. package/dist/test/testFiles/internal/mediaTypeGuessing.test.js +86 -0
  141. package/dist/test/testFiles/sources/bluesky.test.d.ts +2 -0
  142. package/dist/test/testFiles/sources/bluesky.test.d.ts.map +1 -0
  143. package/dist/test/testFiles/sources/bluesky.test.js +40 -0
  144. package/dist/test/testFiles/sources/exampleSource.test.d.ts +2 -0
  145. package/dist/test/testFiles/sources/exampleSource.test.d.ts.map +1 -0
  146. package/dist/test/testFiles/sources/exampleSource.test.js +27 -0
  147. package/dist/test/testFiles/sources/giphy.test.d.ts +2 -0
  148. package/dist/test/testFiles/sources/giphy.test.d.ts.map +1 -0
  149. package/dist/test/testFiles/sources/giphy.test.js +17 -0
  150. package/dist/test/utils/general.d.ts +3 -0
  151. package/dist/test/utils/general.d.ts.map +1 -0
  152. package/dist/test/utils/general.js +24 -0
  153. package/dist/test/utils/globalSetup.d.ts +3 -0
  154. package/dist/test/utils/globalSetup.d.ts.map +1 -0
  155. package/dist/test/utils/globalSetup.js +12 -0
  156. package/dist/test/utils/vitest.d.ts +551 -0
  157. package/dist/test/utils/vitest.d.ts.map +1 -0
  158. package/dist/test/utils/vitest.js +166 -0
  159. package/dist/types.d.ts +16 -0
  160. package/dist/types.d.ts.map +1 -0
  161. package/dist/types.js +1 -0
  162. package/package.json +67 -0
@@ -0,0 +1,23 @@
1
+ import type { OptionsInit as GotOptionsInit } from "got-scraping";
2
+ import type { QueryOptions } from "../schemas/queryOptions.js";
3
+ type CacheableRequest = {
4
+ url: string;
5
+ method: string;
6
+ headers: Record<string, string>;
7
+ body: string;
8
+ headerGeneratorOptions: GotOptionsInit["headerGeneratorOptions"];
9
+ };
10
+ type CachedResponse = {
11
+ body: string;
12
+ statusCode: number;
13
+ headers: Record<string, string>;
14
+ cachedOn: Date;
15
+ request: {
16
+ headers: Record<string, string>;
17
+ };
18
+ };
19
+ export declare function getCachedResponse(req: CacheableRequest): Promise<CachedResponse | undefined>;
20
+ export declare function cacheResponse(req: CacheableRequest, res: Omit<CachedResponse, "cachedOn">): Promise<void>;
21
+ export declare function addCachingFetchWrapper(originalFetch: typeof fetch, cacheNetworkRequests: QueryOptions["cacheNetworkRequests"]): typeof fetch;
22
+ export {};
23
+ //# sourceMappingURL=networkRequestsCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"networkRequestsCache.d.ts","sourceRoot":"","sources":["../../src/lib/networkRequestsCache.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,IAAI,cAAc,EAAE,MAAM,cAAc,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAK/D,KAAK,gBAAgB,GAAG;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IAIb,sBAAsB,EAAE,cAAc,CAAC,wBAAwB,CAAC,CAAC;CAClE,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,QAAQ,EAAE,IAAI,CAAC;IACf,OAAO,EAAE;QACP,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACjC,CAAC;CACH,CAAC;AAEF,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAoBrC;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,gBAAgB,EACrB,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,UAAU,CAAC,iBAQtC;AASD,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,OAAO,KAAK,EAC3B,oBAAoB,EAAE,YAAY,CAAC,sBAAsB,CAAC,GACzD,OAAO,KAAK,CAkDd"}
@@ -0,0 +1,81 @@
1
+ import cacache from "cacache";
2
+ import stringify from "json-stable-stringify";
3
+ import { headersToNormalisedBasicObject, parseFetchArgs } from "./fetch.js";
4
+ const cacheDir = "/tmp/liase/network-requests-cache/custom";
5
+ export async function getCachedResponse(req) {
6
+ const key = getCacheKeyFromReq(req);
7
+ try {
8
+ const { data } = await cacache.get(cacheDir, key);
9
+ const cachedValue = JSON.parse(data.toString());
10
+ return {
11
+ ...cachedValue,
12
+ cachedOn: new Date(cachedValue.cachedOn),
13
+ };
14
+ }
15
+ catch (error) {
16
+ if (typeof error === "object" &&
17
+ error &&
18
+ "code" in error &&
19
+ error.code === "ENOENT") {
20
+ return undefined;
21
+ }
22
+ throw error;
23
+ }
24
+ }
25
+ export async function cacheResponse(req, res) {
26
+ const key = getCacheKeyFromReq(req);
27
+ const value = {
28
+ ...res,
29
+ cachedOn: Date.now(),
30
+ };
31
+ await cacache.put(cacheDir, key, JSON.stringify(value));
32
+ }
33
+ function getCacheKeyFromReq(req) {
34
+ return (stringify([req.url, req.method, Object.entries(req.headers), req.body]) ||
35
+ "");
36
+ }
37
+ export function addCachingFetchWrapper(originalFetch, cacheNetworkRequests) {
38
+ return async (input, init) => {
39
+ if (cacheNetworkRequests === "auto") {
40
+ throw Error(`The "auto" value for the cacheNetworkRequests option is not yet supported. Sorry!`);
41
+ }
42
+ const { url, body, headers, method } = parseFetchArgs(input, init);
43
+ const cacheableRequest = {
44
+ url: url.href,
45
+ body: await body,
46
+ headers,
47
+ method,
48
+ headerGeneratorOptions: undefined,
49
+ };
50
+ let res;
51
+ if (cacheNetworkRequests === "always") {
52
+ res = await getCachedResponse(cacheableRequest);
53
+ if (res) {
54
+ return new Response(res.body, {
55
+ headers: res.headers,
56
+ status: res.statusCode,
57
+ statusText: `Cached on: ${res.cachedOn.getTime()}`,
58
+ });
59
+ }
60
+ }
61
+ else {
62
+ cacheNetworkRequests;
63
+ }
64
+ res = await originalFetch(input, init);
65
+ if (cacheNetworkRequests === "always") {
66
+ const clonedRes = res.clone();
67
+ await cacheResponse(cacheableRequest, {
68
+ statusCode: clonedRes.status,
69
+ body: await clonedRes.text(),
70
+ headers: headersToNormalisedBasicObject(clonedRes.headers),
71
+ request: {
72
+ headers,
73
+ },
74
+ });
75
+ }
76
+ else {
77
+ cacheNetworkRequests;
78
+ }
79
+ return res;
80
+ };
81
+ }
@@ -0,0 +1,21 @@
1
+ export type NetworkRequestsHistoryItem = {
2
+ constructorPath: (string | number)[];
3
+ request: {
4
+ url: URL;
5
+ method: string;
6
+ headers: Record<string, string>;
7
+ body?: string | Promise<string>;
8
+ };
9
+ response: {
10
+ headers: Record<string, string>;
11
+ body: string | Promise<string>;
12
+ statusCode: number;
13
+ cached: boolean;
14
+ cachedOn: Date | null;
15
+ };
16
+ };
17
+ export declare function exportNetworkRequestsHistory({ networkRequestsHistory, }: {
18
+ networkRequestsHistory: NetworkRequestsHistoryItem[];
19
+ }): Promise<void>;
20
+ export declare function exportNetworkRequestsHistoryIfRelevantError(error: unknown): Promise<void>;
21
+ //# sourceMappingURL=networkRequestsHistory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"networkRequestsHistory.d.ts","sourceRoot":"","sources":["../../src/lib/networkRequestsHistory.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,0BAA0B,GAAG;IACvC,eAAe,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IACrC,OAAO,EAAE;QACP,GAAG,EAAE,GAAG,CAAC;QACT,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;KACjC,CAAC;IACF,QAAQ,EAAE;QACR,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QAC/B,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,OAAO,CAAC;QAChB,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;KACvB,CAAC;CACH,CAAC;AAEF,wBAAsB,4BAA4B,CAAC,EACjD,sBAAsB,GACvB,EAAE;IACD,sBAAsB,EAAE,0BAA0B,EAAE,CAAC;CACtD,iBA+FA;AAED,wBAAsB,2CAA2C,CAC/D,KAAK,EAAE,OAAO,iBAgCf"}
@@ -0,0 +1,119 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import chalk from "chalk";
6
+ import mimeTypes from "mime-types";
7
+ import { ActionContext } from "../ActionContext.js";
8
+ import { timeSince } from "./time.js";
9
+ export async function exportNetworkRequestsHistory({ networkRequestsHistory, }) {
10
+ if (!networkRequestsHistory.length) {
11
+ return;
12
+ }
13
+ const tmpDir = path.join(os.tmpdir(), "liase-exports", Date.now().toString());
14
+ try {
15
+ await fs.access(tmpDir);
16
+ }
17
+ catch (error) {
18
+ await fs.mkdir(tmpDir, { recursive: true });
19
+ }
20
+ const constructorPathCount = {};
21
+ for (const networkRequestsHistoryItem of networkRequestsHistory) {
22
+ const constructorPath = networkRequestsHistoryItem.constructorPath.join(".");
23
+ if (!constructorPathCount[constructorPath]) {
24
+ constructorPathCount[constructorPath] = 1;
25
+ }
26
+ else {
27
+ constructorPathCount[constructorPath]++;
28
+ }
29
+ const mimeType = networkRequestsHistoryItem.response.headers["content-type"];
30
+ const ext = mimeTypes.extension(mimeType ?? "") || "txt";
31
+ const constructorPathStr = networkRequestsHistoryItem.constructorPath
32
+ .map((value) => (typeof value === "number" ? `[${value}]` : value))
33
+ .join(".");
34
+ const count = constructorPathCount[constructorPath];
35
+ const urlPart = networkRequestsHistoryItem.request.url.href
36
+ .replaceAll(/(?:^\w+:\/\/|\/+$)/g, "")
37
+ .replaceAll("/", ".");
38
+ const makeBasename = (url) => `${[constructorPathStr, count, url].join("_")}.${ext}`;
39
+ // Most OSes enforce a 255-character limit on filenames. URLs (especially
40
+ // those with long query strings) can easily exceed this, so we truncate the
41
+ // URL portion and append a short hash to keep the name unique.
42
+ const MAX_BASENAME_LENGTH = 242; // 255 minus len(".metadata.txt")
43
+ let basename = makeBasename(urlPart);
44
+ if (basename.length > MAX_BASENAME_LENGTH) {
45
+ const hash = crypto
46
+ .createHash("sha256")
47
+ .update(urlPart)
48
+ .digest("hex")
49
+ .slice(0, 12);
50
+ const prefix = `${[constructorPathStr, count].join("_")}_`;
51
+ const suffix = `_${hash}.${ext}`;
52
+ const availableForUrl = MAX_BASENAME_LENGTH - prefix.length - suffix.length;
53
+ const truncatedUrl = availableForUrl > 0 ? urlPart.slice(0, availableForUrl) : "";
54
+ basename = `${prefix}${truncatedUrl}${suffix}`;
55
+ }
56
+ const filename = path.join(tmpDir, basename);
57
+ let body;
58
+ if (ext === "json") {
59
+ body = JSON.stringify(JSON.parse(await networkRequestsHistoryItem.response.body), null, 2);
60
+ }
61
+ else {
62
+ body = await networkRequestsHistoryItem.response.body;
63
+ }
64
+ await fs.writeFile(filename, body);
65
+ await fs.writeFile(`${filename}.metadata.txt`, [
66
+ `Request URL: ${networkRequestsHistoryItem.request.url.href}`,
67
+ "",
68
+ `Request method: ${networkRequestsHistoryItem.request.method}`,
69
+ "",
70
+ "Request headers:",
71
+ Object.entries(networkRequestsHistoryItem.request.headers)
72
+ .map(([key, value]) => ` ${key}: ${value}`)
73
+ .join("\n"),
74
+ "",
75
+ `Response cached: ${networkRequestsHistoryItem.response.cachedOn
76
+ ? `Yes (${timeSince(networkRequestsHistoryItem.response.cachedOn)})`
77
+ : "No"}`,
78
+ "",
79
+ "Response headers:",
80
+ Object.entries(networkRequestsHistoryItem.response.headers)
81
+ .map(([key, value]) => ` ${key}: ${value}`)
82
+ .join("\n"),
83
+ "",
84
+ `Status code: ${networkRequestsHistoryItem.response.statusCode}`,
85
+ ].join("\n"));
86
+ }
87
+ console.info(chalk.bold("To assist with debugging all requests made with loadUrl have been exported to:"), tmpDir);
88
+ }
89
+ export async function exportNetworkRequestsHistoryIfRelevantError(error) {
90
+ if (!(error instanceof Error)) {
91
+ return;
92
+ }
93
+ let networkRequestsHistory;
94
+ if ("actionContext" in error &&
95
+ error.actionContext instanceof ActionContext) {
96
+ // Most likely error is an instance of ConstructorExecutionError
97
+ networkRequestsHistory = error.actionContext.networkRequestsHistory;
98
+ }
99
+ else if ("context" in error && error.context instanceof ActionContext) {
100
+ // Most likely error is an instance of ZodFriendlyError thrown when parsing the result.
101
+ const rootActionContext = error.context;
102
+ const allActionContexts = [
103
+ rootActionContext,
104
+ ...rootActionContext.descendants,
105
+ ];
106
+ const networkRequestsHistorySet = new Set();
107
+ for (const actionContext of allActionContexts) {
108
+ for (const networkRequestsHistoryItem of actionContext.networkRequestsHistory) {
109
+ networkRequestsHistorySet.add(networkRequestsHistoryItem);
110
+ }
111
+ }
112
+ networkRequestsHistory = [...networkRequestsHistorySet];
113
+ }
114
+ if (networkRequestsHistory) {
115
+ await exportNetworkRequestsHistory({
116
+ networkRequestsHistory,
117
+ });
118
+ }
119
+ }
@@ -0,0 +1,2 @@
1
+ export declare function timeSince(date: Date): string;
2
+ //# sourceMappingURL=time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../src/lib/time.ts"],"names":[],"mappings":"AAKA,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,UA4BnC"}
@@ -0,0 +1,31 @@
1
+ const timePeriodFormatter = new Intl.RelativeTimeFormat("en", {
2
+ localeMatcher: "best fit",
3
+ numeric: "auto",
4
+ });
5
+ export function timeSince(date) {
6
+ if (Number.isNaN(date.getTime())) {
7
+ throw Error("Invalid date");
8
+ }
9
+ const seconds = Math.floor((date.getTime() - Date.now()) / 1000);
10
+ let interval = seconds / 31536000;
11
+ if (interval < -1) {
12
+ return timePeriodFormatter.format(Math.trunc(interval), "year");
13
+ }
14
+ interval = seconds / 2592000;
15
+ if (interval < -1) {
16
+ return timePeriodFormatter.format(Math.trunc(interval), "month");
17
+ }
18
+ interval = seconds / 86400;
19
+ if (interval < -1) {
20
+ return timePeriodFormatter.format(Math.trunc(interval), "day");
21
+ }
22
+ interval = seconds / 3600;
23
+ if (interval < -1) {
24
+ return timePeriodFormatter.format(Math.trunc(interval), "hours");
25
+ }
26
+ interval = seconds / 60;
27
+ if (interval < -1) {
28
+ return timePeriodFormatter.format(Math.trunc(interval), "minutes");
29
+ }
30
+ return timePeriodFormatter.format(Math.trunc(interval), "seconds");
31
+ }
@@ -0,0 +1,46 @@
1
+ import type { ActionContext } from "../ActionContext.js";
2
+ export declare function createCounter(): () => number;
3
+ export declare function getRandomIntOfScale(scale?: number): number;
4
+ export declare function getRandomIntInRange(min?: number, max?: number): number;
5
+ export declare const getUniqueId: () => string;
6
+ export declare function mergeInUnsetProperties(a: object, b: object): {};
7
+ export declare const formatObjectPath: (path: (string | number)[]) => string;
8
+ export declare const formatObjectPathAsTree: (path: (string | number)[], options: {
9
+ lineIndent?: number;
10
+ overallIndent?: number;
11
+ lastNodeExtraInfo?: string;
12
+ }) => string;
13
+ declare const returnType: (v: unknown) => "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function";
14
+ type TypeOfTypes = ReturnType<typeof returnType>;
15
+ export declare const capitaliseType: (type: TypeOfTypes | "array" | "set" | "date") => string;
16
+ type ConstructorExecutionErrorOptions = {
17
+ cause?: Error;
18
+ log: string[];
19
+ message?: string;
20
+ actionContext: ActionContext;
21
+ };
22
+ export declare class ConstructorExecutionError extends Error {
23
+ errorOccurredAtPath: (string | number)[];
24
+ log: string[];
25
+ actionContext: ActionContext;
26
+ constructor({ message, cause, actionContext, log, }: ConstructorExecutionErrorOptions);
27
+ getFormattedErrorInfo(): string;
28
+ }
29
+ type ValueType = "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | "array";
30
+ export declare const getType: (value: unknown) => ValueType;
31
+ export declare function hasNoDuplicates(array: unknown[]): boolean;
32
+ export declare function getDuplicates<T>(array: T[]): T[];
33
+ export declare function getOrdinal(number: number): string;
34
+ export declare function getPromiseWithResolvers<Expected = unknown>(): {
35
+ promise: Promise<Expected>;
36
+ resolve: (value: Expected) => void;
37
+ reject: (reason?: any) => void;
38
+ };
39
+ export type ObjectEntry<BaseType> = [keyof BaseType, BaseType[keyof BaseType]];
40
+ export declare function waitForAllPropertiesToResolve<ObjectWithPromises>(object: {
41
+ [key in keyof ObjectWithPromises]: ObjectWithPromises[key];
42
+ }): Promise<{
43
+ [key in keyof ObjectWithPromises]: Awaited<ObjectWithPromises[key]>;
44
+ }>;
45
+ export {};
46
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,wBAAgB,aAAa,iBAM5B;AAKD,wBAAgB,mBAAmB,CAAC,KAAK,SAAI,UAI5C;AAGD,wBAAgB,mBAAmB,CAAC,GAAG,SAAI,EAAE,GAAG,SAAU,UAIzD;AAED,eAAO,MAAM,WAAW,cAGpB,CAAC;AAGL,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,MAE1D;AAED,eAAO,MAAM,gBAAgB,GAAI,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,WAK3C,CAAC;AAEhB,eAAO,MAAM,sBAAsB,GACjC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,EACzB,SAAS;IACP,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,WA0CF,CAAC;AAKF,QAAA,MAAM,UAAU,GAAI,GAAG,OAAO,gGAAa,CAAC;AAC5C,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAEjD,eAAO,MAAM,cAAc,GACzB,MAAM,WAAW,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,KAC3C,MAoBF,CAAC;AAEF,KAAK,gCAAgC,GAAG;IACtC,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,aAAa,CAAC;CAC9B,CAAC;AACF,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,mBAAmB,sBAAC;IACpB,GAAG,WAAC;IACJ,aAAa,gBAAC;gBAEF,EACV,OAAO,EACP,KAAK,EACL,aAAa,EACb,GAAG,GACJ,EAAE,gCAAgC;IAoBnC,qBAAqB;CAqBtB;AAED,KAAK,SAAS,GACV,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,WAAW,GACX,QAAQ,GACR,UAAU,GACV,OAAO,CAAC;AAEZ,eAAO,MAAM,OAAO,GAAI,OAAO,OAAO,KAAG,SAKxC,CAAC;AAEF,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAEzD;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAUhD;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,UAgBxC;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,GAAG,OAAO;;qBACnC,QAAQ,KAAK,IAAI;sBAEhB,GAAG,KAAK,IAAI;EAcnC;AAED,MAAM,MAAM,WAAW,CAAC,QAAQ,IAAI,CAAC,MAAM,QAAQ,EAAE,QAAQ,CAAC,MAAM,QAAQ,CAAC,CAAC,CAAC;AAG/E,wBAAsB,6BAA6B,CAAC,kBAAkB,EACpE,MAAM,EAAE;KACL,GAAG,IAAI,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,GAAG,CAAC;CAC3D,GACA,OAAO,CAAC;KACR,GAAG,IAAI,MAAM,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC;CACpE,CAAC,CAqBD"}
@@ -0,0 +1,208 @@
1
+ import chalk from "chalk";
2
+ export function createCounter() {
3
+ let counter = -1;
4
+ return () => {
5
+ counter = counter === Number.MAX_SAFE_INTEGER ? 0 : counter + 1;
6
+ return counter;
7
+ };
8
+ }
9
+ // Now that getUniqueId uses a counter to ensure there are no id clashes due to having the same timestamp,
10
+ // we don't really need to use a random int but I had fun writing these functions and don't want to get rid
11
+ // of them. So until there's a reason to I'm not going to.
12
+ export function getRandomIntOfScale(scale = 6) {
13
+ const min = 10 ** (scale - 1);
14
+ const max = 10 ** scale - 1;
15
+ return getRandomIntInRange(min, max);
16
+ }
17
+ // Both min and max are inclusive
18
+ export function getRandomIntInRange(min = 0, max = 999_999) {
19
+ return Math.floor(Math.random() * (Math.floor(max) - Math.ceil(min) + 1) + Math.ceil(min));
20
+ }
21
+ export const getUniqueId = (() => {
22
+ const counter = createCounter();
23
+ return () => `${Date.now()}-${counter()}-${getRandomIntOfScale(6)}`;
24
+ })();
25
+ // Used to increase readability in some places
26
+ export function mergeInUnsetProperties(a, b) {
27
+ return { ...b, ...a };
28
+ }
29
+ export const formatObjectPath = (path) => `$${path
30
+ .map((segment) => typeof segment === "number" ? `[${segment}]` : `.${segment}`)
31
+ .join("")}`;
32
+ export const formatObjectPathAsTree = (path, options) => {
33
+ const lineIndent = options.lineIndent ?? 2;
34
+ const overallIndent = options.overallIndent ?? 0;
35
+ const lastNodeExtraInfo = options.lastNodeExtraInfo ?? "";
36
+ if (lineIndent < 2) {
37
+ throw Error("Line indent can not be less than 2");
38
+ }
39
+ let formattedTree = path
40
+ .reduce((segments, segment) => {
41
+ if (typeof segment === "number") {
42
+ const formattedSegment = `[${segment}]`;
43
+ if (segments.length) {
44
+ segments[segments.length - 1] += formattedSegment;
45
+ }
46
+ else {
47
+ segments.push(formattedSegment);
48
+ }
49
+ }
50
+ else {
51
+ segments.push(segment);
52
+ }
53
+ return segments;
54
+ }, [])
55
+ // If segment is a number then wrap square brackets around it to signal that
56
+ .map((segment) => typeof segment === "number" ? `[${segment}]` : `${segment}`)
57
+ .map((segment) => chalk.bold(segment))
58
+ // Add arrow between segments
59
+ // Indent each segment by lineIndent more than the last + add arrow between segments
60
+ .map((segment, index) => (index ? " ".repeat(index * lineIndent - 2) + chalk.dim("↳ ") : "") +
61
+ segment)
62
+ .join("\n");
63
+ if (lastNodeExtraInfo) {
64
+ formattedTree = `${formattedTree} - ${lastNodeExtraInfo.replace(/\n/, `\n${" ".repeat((path.length - 1) * lineIndent)}`)}`;
65
+ }
66
+ return formattedTree
67
+ .split("\n")
68
+ .map((line) => " ".repeat(overallIndent) + line)
69
+ .join("\n");
70
+ };
71
+ const capitalisedFirstLetter = (string) => string[0].toUpperCase() + string.substring(1);
72
+ const returnType = (v) => typeof v;
73
+ export const capitaliseType = (type) => {
74
+ let result;
75
+ switch (type) {
76
+ case "string":
77
+ case "number":
78
+ case "boolean":
79
+ case "symbol":
80
+ case "undefined":
81
+ case "object":
82
+ case "function":
83
+ case "array":
84
+ case "set":
85
+ case "date":
86
+ result = capitalisedFirstLetter(type);
87
+ break;
88
+ case "bigint":
89
+ result = "BigInt";
90
+ break;
91
+ }
92
+ return result;
93
+ };
94
+ export class ConstructorExecutionError extends Error {
95
+ errorOccurredAtPath;
96
+ log;
97
+ actionContext;
98
+ constructor({ message, cause, actionContext, log, }) {
99
+ super(message ?? cause?.message ?? "Error when executing constructor");
100
+ if (cause) {
101
+ this.stack = cause.stack;
102
+ }
103
+ // Use class name as the name of the error
104
+ ConstructorExecutionError.prototype.name = this.constructor.name;
105
+ this.errorOccurredAtPath = actionContext.path;
106
+ this.log = log;
107
+ this.actionContext = actionContext;
108
+ this.message = this.getFormattedErrorInfo();
109
+ // Explicitly set the prototype to maintain the correct prototype chain is
110
+ // required for "instanceOf" to work as expected
111
+ Object.setPrototypeOf(this, ConstructorExecutionError.prototype);
112
+ }
113
+ getFormattedErrorInfo() {
114
+ const stack = this.cause instanceof Error
115
+ ? this.cause.stack?.replace(`${this.cause.toString()}\n`, "")
116
+ : this.stack;
117
+ const lastConstructorProp = this.errorOccurredAtPath[this.errorOccurredAtPath.length - 1];
118
+ const errorLocation = stack
119
+ ?.split("\n")
120
+ .find((line) => line.startsWith(` at ${lastConstructorProp}`))
121
+ ?.match(/\((.*)\)/)?.[1];
122
+ return [
123
+ "Failed to render the following response constructor property",
124
+ formatObjectPathAsTree(this.errorOccurredAtPath, {
125
+ lineIndent: 3,
126
+ overallIndent: 2,
127
+ lastNodeExtraInfo: `${chalk.bold(this.message)}\n(${chalk.blue(errorLocation)})`,
128
+ }),
129
+ "",
130
+ ].join("\n");
131
+ }
132
+ }
133
+ export const getType = (value) => {
134
+ if (Array.isArray(value)) {
135
+ return "array";
136
+ }
137
+ return typeof value;
138
+ };
139
+ export function hasNoDuplicates(array) {
140
+ return new Set(array).size === array.length;
141
+ }
142
+ export function getDuplicates(array) {
143
+ const duplicates = new Set();
144
+ for (let i = 0; i < array.length; i++) {
145
+ for (let j = i + 1; j < array.length; j++) {
146
+ if (array[i] === array[j]) {
147
+ duplicates.add(array[i]);
148
+ }
149
+ }
150
+ }
151
+ return [...duplicates];
152
+ }
153
+ export function getOrdinal(number) {
154
+ let suffix;
155
+ switch (number % 10) {
156
+ case 1:
157
+ suffix = "st";
158
+ break;
159
+ case 2:
160
+ suffix = "nd";
161
+ break;
162
+ case 3:
163
+ suffix = "rd";
164
+ break;
165
+ default:
166
+ suffix = "th";
167
+ }
168
+ return `${number}${suffix}`;
169
+ }
170
+ export function getPromiseWithResolvers() {
171
+ let resolve;
172
+ // biome-ignore lint/suspicious/noExplicitAny: reject reason is intentionally untyped
173
+ let reject;
174
+ const promise = new Promise((...resolvers) => {
175
+ resolve = resolvers[0];
176
+ reject = resolvers[1];
177
+ });
178
+ // @ts-expect-error The point of this is to make sure the resolvers are defined
179
+ if (typeof resolve === "undefined" || typeof reject === "undefined") {
180
+ throw Error("Resolvers not set yet");
181
+ }
182
+ return {
183
+ promise,
184
+ resolve,
185
+ reject,
186
+ };
187
+ }
188
+ // Takes an object and if any property values are promises it will wait until they're resolved.
189
+ export async function waitForAllPropertiesToResolve(object) {
190
+ // So that we can use Promise.all() to resolve every prop in object we first convert
191
+ // object to an array of entries, then we swap each entry with a promise that resolves
192
+ // to the entry. That way we can can use Promise.all() on it to get us back to an array
193
+ // of entries (but now with any promises resolved), then use Object.fromEntry() to
194
+ // re-assemble the entries back into an object.
195
+ const entriesAsPromises = Object.entries(object).map((entry) => {
196
+ const [entryKey, entryValue] = entry;
197
+ if (entryValue instanceof Promise) {
198
+ return entryValue.then((resolvedEntryValue) => [
199
+ entryKey,
200
+ resolvedEntryValue,
201
+ ]);
202
+ }
203
+ // biome-ignore lint/suspicious/noExplicitAny: entry tuple cast needed for Promise.all compatibility
204
+ return new Promise((resolve) => resolve(entry));
205
+ });
206
+ const entriesResolved = await Promise.all(entriesAsPromises);
207
+ return Object.fromEntries(entriesResolved);
208
+ }
@@ -0,0 +1,35 @@
1
+ import type { z } from "zod";
2
+ export declare function zodParseOrThrow<Output, Def extends z.ZodTypeDef, Input>(zodSchema: z.ZodType<Output, Def, Input>, input: any, options?: {
3
+ errorMessage?: string;
4
+ context?: unknown;
5
+ }): Output;
6
+ type ValueType = "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | "array";
7
+ export declare const getType: (value: unknown) => ValueType;
8
+ type FriendlyZodErrorOptions = {
9
+ message?: string;
10
+ inputData?: unknown;
11
+ context?: unknown;
12
+ };
13
+ type FriendlyZodErrorIssue = {
14
+ zodIssue: z.ZodIssue;
15
+ depth: number;
16
+ formattedMessage: string;
17
+ };
18
+ type FriendlyZodErrorIssuesTree = {
19
+ issues: FriendlyZodErrorIssue[];
20
+ children: Record<string | number, FriendlyZodErrorIssuesTree>;
21
+ };
22
+ export declare class FriendlyZodError extends Error {
23
+ #private;
24
+ zodError: z.ZodError;
25
+ context: unknown;
26
+ constructor(error: z.ZodError, { message, inputData, context }?: FriendlyZodErrorOptions);
27
+ formatZodIssue(issue: z.ZodIssue, includePath?: boolean): string;
28
+ formatZodErrorIssues(error?: z.ZodError, depth?: number, includePath?: boolean): FriendlyZodErrorIssue[];
29
+ formatZodErrorIssuesAsDotPoints(error?: z.ZodError, depth?: number, indentSize?: number): string;
30
+ formatZodErrorIssuesAsTree(error?: z.ZodError): FriendlyZodErrorIssuesTree;
31
+ formatZodErrorIssuesAsTreeString(error?: z.ZodError, depth?: number, indentSize?: number): string;
32
+ get formattedErrorInfo(): string;
33
+ }
34
+ export {};
35
+ //# sourceMappingURL=zod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod.d.ts","sourceRoot":"","sources":["../../src/lib/zod.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAO7B,wBAAgB,eAAe,CAAC,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,UAAU,EAAE,KAAK,EACrE,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,EAExC,KAAK,EAAE,GAAG,EACV,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GACzD,MAAM,CAiBR;AAED,KAAK,SAAS,GACV,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,WAAW,GACX,QAAQ,GACR,UAAU,GACV,OAAO,CAAC;AAEZ,eAAO,MAAM,OAAO,GAAI,OAAO,OAAO,KAAG,SAKxC,CAAC;AA0CF,KAAK,uBAAuB,GAAG;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AACF,KAAK,0BAA0B,GAAG;IAChC,MAAM,EAAE,qBAAqB,EAAE,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,0BAA0B,CAAC,CAAC;CAC/D,CAAC;AACF,qBAAa,gBAAiB,SAAQ,KAAK;;IAEzC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;gBAGf,KAAK,EAAE,CAAC,CAAC,QAAQ,EACjB,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,GAAE,uBAA4B;IAqB/D,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,EAAE,WAAW,UAAO,GAAG,MAAM;IAyF7D,oBAAoB,CAClB,KAAK,GAAE,CAAC,CAAC,QAAwB,EACjC,KAAK,SAAI,EACT,WAAW,UAAO,GACjB,qBAAqB,EAAE;IAsD1B,+BAA+B,CAC7B,KAAK,GAAE,CAAC,CAAC,QAAwB,EACjC,KAAK,SAAI,EACT,UAAU,SAAI,GACb,MAAM;IAST,0BAA0B,CACxB,KAAK,GAAE,CAAC,CAAC,QAAwB,GAChC,0BAA0B;IAqC7B,gCAAgC,CAC9B,KAAK,GAAE,CAAC,CAAC,QAAwB,EACjC,KAAK,SAAI,EACT,UAAU,SAAI,GACb,MAAM;IAgCT,IAAI,kBAAkB,IAAI,MAAM,CAY/B;CACF"}