@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,227 @@
1
+ import { decodeHTML } from "entities";
2
+ import { guessMediaInfoFromMimeType, guessMediaInfoFromUrl, } from "./actionHelpers.js";
3
+ import { generateResponse, getResponseDetailsBasedOnRequest, } from "./generateResponse.js";
4
+ import { headersToNormalisedBasicObject, parseFetchArgs } from "./lib/fetch.js";
5
+ import { addCachingFetchWrapper } from "./lib/networkRequestsCache.js";
6
+ import { loadUrl } from "./loadUrl.js";
7
+ export const excludeFieldSymbol = Symbol("ExcludeField");
8
+ export class ActionContext extends Function {
9
+ constructor(args) {
10
+ super();
11
+ this.#constructorContext = args.constructorContext;
12
+ this.#executeActions = args.executeActions;
13
+ this.#path = args.path;
14
+ if (args.initialData) {
15
+ this.#dataStore = args.initialData;
16
+ }
17
+ this.#networkRequestsHistory = args.networkRequestsHistory ?? [];
18
+ // biome-ignore lint/correctness/noConstructorReturn: Proxy wrapping requires returning from constructor
19
+ return new Proxy(this, {
20
+ apply: (target, thisArg, args) => target.get(...args),
21
+ get: (target, propName, receiver) => {
22
+ const value = target[propName];
23
+ if (value instanceof Function) {
24
+ // biome-ignore lint/suspicious/noExplicitAny: spread args for dynamic proxy forwarding
25
+ return (...args) => value.apply(this === receiver ? target : this, args);
26
+ }
27
+ return value;
28
+ },
29
+ });
30
+ }
31
+ #constructorContext;
32
+ #executeActions;
33
+ #path;
34
+ // biome-ignore lint/suspicious/noExplicitAny: result history stores arbitrary action results
35
+ #resultHistory = [];
36
+ #networkRequestsHistory;
37
+ // eslint-disable-next-line no-use-before-define -- we need to use before it's defined since it's recursive
38
+ #clonedChildren = [];
39
+ // biome-ignore lint/suspicious/noExplicitAny: unresolved promise store accepts arbitrary values
40
+ #unresolvedPromises = [];
41
+ // biome-ignore lint/suspicious/noExplicitAny: data store accepts arbitrary values
42
+ #dataStore = {};
43
+ get(key = "") {
44
+ if (!(key in this.#dataStore)) {
45
+ throw Error(`Attempted to access value "${key}" but that value was never set`);
46
+ }
47
+ return this.#dataStore[key];
48
+ }
49
+ // biome-ignore lint/suspicious/noExplicitAny: value can be any type in dynamic data store
50
+ set(key, value) {
51
+ if (value instanceof Promise) {
52
+ this.#unresolvedPromises.push(value.then((resolvedValue) => {
53
+ if (this.get(key) === value) {
54
+ this.set(key, resolvedValue);
55
+ }
56
+ else {
57
+ throw Error(`The value saved under the key "${key}" was changed before the original value (which was a promise) finished resolving.`);
58
+ }
59
+ }));
60
+ }
61
+ this.#dataStore[key] = value;
62
+ return this;
63
+ }
64
+ has(key = "") {
65
+ return key in this.#dataStore;
66
+ }
67
+ getAll() {
68
+ return { ...this.#dataStore };
69
+ }
70
+ // biome-ignore lint/suspicious/noExplicitAny: result can be any type from dynamic action execution
71
+ recordResult(result) {
72
+ this.#resultHistory.push(result);
73
+ }
74
+ // biome-ignore lint/suspicious/noExplicitAny: result can be any type from dynamic action execution
75
+ lastResult() {
76
+ return this.#resultHistory[this.#resultHistory.length - 1];
77
+ }
78
+ clone({ path, appendToPath, data, } = {}) {
79
+ const clone = new ActionContext({
80
+ constructorContext: this.#constructorContext,
81
+ initialData: data ? { ...data } : { ...this.#dataStore },
82
+ executeActions: this.#executeActions,
83
+ path: (path ?? this.#path).concat(appendToPath ?? []),
84
+ networkRequestsHistory: this.#networkRequestsHistory,
85
+ });
86
+ this.#clonedChildren.push(clone);
87
+ return clone;
88
+ }
89
+ get descendants() {
90
+ return this.#clonedChildren.flatMap((clonedChild) => [
91
+ clonedChild,
92
+ ...clonedChild.descendants,
93
+ ]);
94
+ }
95
+ chain(...actions) {
96
+ return this.#executeActions(actions, this, this.#path);
97
+ }
98
+ waitForAllPromisesToResolve() {
99
+ return Promise.all(this.#unresolvedPromises);
100
+ }
101
+ // biome-ignore lint/suspicious/noExplicitAny: request is a dynamic object from user input
102
+ get request() {
103
+ return Object.freeze(this.#constructorContext.request);
104
+ }
105
+ // biome-ignore lint/suspicious/noExplicitAny: secrets is a dynamic object from user input
106
+ get secrets() {
107
+ return Object.freeze(this.#constructorContext.secrets);
108
+ }
109
+ get requestHandler() {
110
+ return Object.freeze(this.#constructorContext.requestHandler);
111
+ }
112
+ get pageFetchLimitReached() {
113
+ return this.#constructorContext.pageFetchLimitReached;
114
+ }
115
+ get cacheNetworkRequests() {
116
+ return this.#constructorContext.cacheNetworkRequests;
117
+ }
118
+ loadUrl = (async (url, options) => {
119
+ const response = (await loadUrl.call(this, url,
120
+ // @ts-expect-error -- Not sure why ts doesn't like this being undefined
121
+ options));
122
+ let stringifiedBody;
123
+ if ("root" in response) {
124
+ stringifiedBody = response.root.nativeSelector.html() ?? "";
125
+ }
126
+ else if (typeof response.data === "string") {
127
+ stringifiedBody = response.data;
128
+ }
129
+ else {
130
+ stringifiedBody = JSON.stringify(response.data, null, 2);
131
+ }
132
+ this.#networkRequestsHistory.push({
133
+ constructorPath: this.#path,
134
+ request: {
135
+ url: new URL(url),
136
+ method: options?.method || "GET",
137
+ headers: response.request.headers,
138
+ body: options?.body,
139
+ },
140
+ response: {
141
+ headers: response.headers,
142
+ body: stringifiedBody,
143
+ statusCode: response.statusCode,
144
+ cached: response.cached,
145
+ cachedOn: response.cachedOn,
146
+ },
147
+ });
148
+ return response;
149
+ // biome-ignore lint/suspicious/noExplicitAny: cast needed for complex overloaded function assignment
150
+ });
151
+ get networkRequestsHistory() {
152
+ return this.#networkRequestsHistory;
153
+ }
154
+ loadRequest = async (requestHandler, request, { secrets = {}, } = {}) => {
155
+ const sourceId = request.source ?? this.#constructorContext.sourceId;
156
+ const fullRequest = {
157
+ ...request,
158
+ source: sourceId,
159
+ queryType: request.queryType ?? requestHandler.id,
160
+ };
161
+ const constructorContext = {
162
+ request: fullRequest,
163
+ secrets,
164
+ requestHandler,
165
+ responseDetails: getResponseDetailsBasedOnRequest(requestHandler.responses, fullRequest),
166
+ sourceId,
167
+ hooks: this.#constructorContext.hooks,
168
+ };
169
+ return generateResponse(constructorContext);
170
+ };
171
+ get hooks() {
172
+ return this.#constructorContext.hooks;
173
+ }
174
+ get fetch() {
175
+ const cachingFetch = addCachingFetchWrapper(fetch, this.#constructorContext.cacheNetworkRequests);
176
+ return async (input, init) => {
177
+ const response = await cachingFetch(input, init);
178
+ const { url, body, headers, method } = parseFetchArgs(input, init);
179
+ const clonedResponse = response.clone();
180
+ const cacheTimestamp = response.statusText.match(/^Cached on: (\d+)$/)?.[1];
181
+ const responseHeaders = headersToNormalisedBasicObject(clonedResponse.headers);
182
+ this.#networkRequestsHistory.push({
183
+ constructorPath: this.#path,
184
+ request: {
185
+ url,
186
+ method,
187
+ headers,
188
+ body,
189
+ },
190
+ response: {
191
+ headers: responseHeaders,
192
+ body: clonedResponse.text(),
193
+ statusCode: clonedResponse.status,
194
+ cached: Boolean(cacheTimestamp),
195
+ cachedOn: cacheTimestamp
196
+ ? new Date(Number.parseInt(cacheTimestamp))
197
+ : null,
198
+ },
199
+ });
200
+ return response;
201
+ };
202
+ }
203
+ guessMediaInfoFromUrl = guessMediaInfoFromUrl;
204
+ guessMediaInfoFromMimeType = guessMediaInfoFromMimeType;
205
+ decodeHTML = (value) => decodeHTML(value);
206
+ durationStringToNumber = (duration) => {
207
+ const match = duration.match(/^\s*(?:(?:(\d+)[:D])?(\d{1,2})[:H])?(\d{1,2})[:M](\d{2})[S]?\s*$/);
208
+ if (!match) {
209
+ throw Error(`The value "${duration}" is not a valid duration string`);
210
+ }
211
+ const [, ...segments] = match;
212
+ let totalSeconds = 0;
213
+ if (typeof segments[0] === "string") {
214
+ totalSeconds += Number.parseInt(segments[0]) * 24 * 60 * 60;
215
+ }
216
+ if (typeof segments[1] === "string") {
217
+ totalSeconds += Number.parseInt(segments[1]) * 60 * 60;
218
+ }
219
+ totalSeconds += Number.parseInt(segments[2]) * 60;
220
+ totalSeconds += Number.parseInt(segments[3]);
221
+ return totalSeconds;
222
+ };
223
+ excludeField = excludeFieldSymbol;
224
+ get path() {
225
+ return this.#path;
226
+ }
227
+ }
@@ -0,0 +1,30 @@
1
+ import type { Cheerio, CheerioAPI } from "cheerio";
2
+ import type { AnyNode } from "domhandler";
3
+ export declare abstract class DomSelection {
4
+ abstract select: (selector: string) => DomSelection;
5
+ abstract attr: (attr: string) => string | undefined;
6
+ abstract exists: (selector: string) => boolean;
7
+ abstract get text(): string | Promise<string>;
8
+ abstract get nativeSelector(): Cheerio<AnyNode>;
9
+ abstract get selectedNodes(): DomSelection[];
10
+ abstract get firstJsonLd(): Record<string, unknown>;
11
+ abstract get jsonLd(): Array<Record<string, unknown>>;
12
+ map<T>(mapFunction: (node: DomSelection, index: number) => T): T[];
13
+ }
14
+ export declare class CheerioDomSelection extends DomSelection {
15
+ #private;
16
+ constructor(cheerioAPI: CheerioAPI, cheerioNode?: Cheerio<AnyNode>);
17
+ select: (selector: string) => CheerioDomSelection;
18
+ attr: (attr: string) => string | undefined;
19
+ exists: (selector: string) => boolean;
20
+ get nativeSelector(): Cheerio<AnyNode>;
21
+ get text(): string;
22
+ map<T>(mapFunction: (node: CheerioDomSelection, index: number) => T): T[];
23
+ get selectedNodes(): Array<CheerioDomSelection>;
24
+ get firstJsonLd(): Record<string, unknown>;
25
+ get canonicalUrl(): string | undefined;
26
+ get jsonLd(): Array<Record<string, unknown>>;
27
+ get data(): Record<string, unknown>;
28
+ get value(): string | undefined | string[];
29
+ }
30
+ //# sourceMappingURL=DomSelection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DomSelection.d.ts","sourceRoot":"","sources":["../src/DomSelection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,8BAAsB,YAAY;IAEhC,QAAQ,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,YAAY,CAAC;IACpD,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACpD,QAAQ,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IAC/C,QAAQ,KAAK,IAAI,IAAI,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C,QAAQ,KAAK,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAChD,QAAQ,KAAK,aAAa,IAAI,YAAY,EAAE,CAAC;IAC7C,QAAQ,KAAK,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpD,QAAQ,KAAK,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,GAAG,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC,GAAG,CAAC,EAAE;CAGnE;AAED,qBAAa,mBAAoB,SAAQ,YAAY;;gBAKvC,UAAU,EAAE,UAAU,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC;IAMlE,MAAM,GAAI,UAAU,MAAM,yBAC6C;IAEvE,IAAI,GAAI,MAAM,MAAM,wBAAqC;IACzD,MAAM,GAAI,UAAU,MAAM,aAC4B;IAEtD,IAAI,cAAc,qBAEjB;IAED,IAAI,IAAI,WAEP;IAED,GAAG,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC,GAAG,CAAC,EAAE;IAIzE,IAAI,aAAa,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAI9C;IAED,IAAI,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzC;IAED,IAAI,YAAY,IAAI,MAAM,GAAG,SAAS,CAErC;IAED,IAAI,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAiB3C;IAED,IAAI,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAElC;IAED,IAAI,KAAK,IAAI,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAEzC;CACF"}
@@ -0,0 +1,60 @@
1
+ export class DomSelection {
2
+ map(mapFunction) {
3
+ return this.selectedNodes.map(mapFunction);
4
+ }
5
+ }
6
+ export class CheerioDomSelection extends DomSelection {
7
+ #nativeSelector;
8
+ #cachedJsonLdArray;
9
+ #$;
10
+ constructor(cheerioAPI, cheerioNode) {
11
+ super();
12
+ this.#nativeSelector = cheerioNode ?? cheerioAPI.root();
13
+ this.#$ = cheerioAPI;
14
+ }
15
+ select = (selector) => new CheerioDomSelection(this.#$, this.nativeSelector.find(selector));
16
+ attr = (attr) => this.#nativeSelector.attr(attr);
17
+ exists = (selector) => Boolean(this.#nativeSelector.find(selector).length);
18
+ get nativeSelector() {
19
+ return this.#nativeSelector;
20
+ }
21
+ get text() {
22
+ return this.#nativeSelector.text();
23
+ }
24
+ map(mapFunction) {
25
+ return this.selectedNodes.map(mapFunction);
26
+ }
27
+ get selectedNodes() {
28
+ return this.#nativeSelector
29
+ .toArray()
30
+ .map((node) => new CheerioDomSelection(this.#$, this.#$(node)));
31
+ }
32
+ get firstJsonLd() {
33
+ return this.jsonLd[0];
34
+ }
35
+ get canonicalUrl() {
36
+ return this.select("link[rel=canonical]").attr("href");
37
+ }
38
+ get jsonLd() {
39
+ if (!this.#cachedJsonLdArray) {
40
+ this.#cachedJsonLdArray = this.select('script[type="application/ld+json"]')
41
+ .map((ldJsonElm) => ldJsonElm.text)
42
+ .map((ldJsonText) => {
43
+ try {
44
+ return JSON.parse(ldJsonText);
45
+ }
46
+ catch (error) {
47
+ console.error("Text is not valid JSON:", ldJsonText);
48
+ throw error;
49
+ }
50
+ });
51
+ }
52
+ return this.#cachedJsonLdArray;
53
+ }
54
+ get data() {
55
+ return this.#nativeSelector.data();
56
+ }
57
+ get value() {
58
+ return this.#nativeSelector.val();
59
+ }
60
+ }
@@ -0,0 +1,40 @@
1
+ import { type FinderOptions, type FinderOptionsInput } from "./schemas/finderOptions.js";
2
+ import type { Plugin } from "./schemas/plugin.js";
3
+ import type { Source } from "./schemas/source.js";
4
+ import type { LiaseHooks } from "./lib/hooks.js";
5
+ import { type RequestHandler } from "./schemas/requestHandler.js";
6
+ export default class Liase {
7
+ protected sourceMap: {
8
+ [sourceName: string]: Source;
9
+ };
10
+ get sources(): Source[];
11
+ _finderOptions: FinderOptions;
12
+ _hooks: LiaseHooks;
13
+ constructor(finderOptions?: FinderOptionsInput);
14
+ loadPlugin(plugin: Plugin): void;
15
+ loadSources(sources: Source[]): void;
16
+ loadSource(source: Source): void;
17
+ loadHooks(hooks: NonNullable<Plugin["hooks"]>): void;
18
+ getSource(sourceId: string): Source;
19
+ getRequestHandler(sourceId: string, queryType: string): RequestHandler;
20
+ getRequestSchema(sourceId: string, queryType: string): import("zod").ZodObject<{
21
+ source: import("zod").ZodString;
22
+ queryType: import("zod").ZodString;
23
+ pageNumber?: import("zod").ZodTypeAny;
24
+ cursor?: import("zod").ZodTypeAny;
25
+ }, "strict", import("zod").ZodTypeAny, Omit<{
26
+ source: string;
27
+ queryType: string;
28
+ pageNumber?: unknown;
29
+ cursor?: unknown;
30
+ }, "pageNumber" | "cursor"> & {
31
+ pageNumber?: number;
32
+ cursor?: string | number | null;
33
+ }, {
34
+ source: string;
35
+ queryType: string;
36
+ pageNumber?: unknown;
37
+ cursor?: unknown;
38
+ }>;
39
+ }
40
+ //# sourceMappingURL=Liase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Liase.d.ts","sourceRoot":"","sources":["../src/Liase.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,kBAAkB,EAExB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGjD,OAAO,EACL,KAAK,cAAc,EAEpB,MAAM,6BAA6B,CAAC;AAErC,MAAM,CAAC,OAAO,OAAO,KAAK;IACxB,SAAS,CAAC,SAAS,EAAE;QAAE,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAM;IAC3D,IAAI,OAAO,IAAI,MAAM,EAAE,CAEtB;IAED,cAAc,EAAE,aAAa,CAAC;IAC9B,MAAM,EAAE,UAAU,CAGhB;gBAEU,aAAa,GAAE,kBAAuB;IAQlD,UAAU,CAAC,MAAM,EAAE,MAAM;IASzB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE;IAM7B,UAAU,CAAC,MAAM,EAAE,MAAM;IAmEzB,SAAS,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI;IAUpD,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAUnC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,cAAc;IAatE,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;CAGrD"}
package/dist/Liase.js ADDED
@@ -0,0 +1,105 @@
1
+ import { finderOptionsSchema, } from "./schemas/finderOptions.js";
2
+ import { zodParseOrThrow } from "./lib/zod.js";
3
+ import builtInSourcesPlugin from "./plugins/built-in-sources/index.js";
4
+ import { requestHandlerSchema, } from "./schemas/requestHandler.js";
5
+ export default class Liase {
6
+ sourceMap = {};
7
+ get sources() {
8
+ return Object.values(this.sourceMap);
9
+ }
10
+ _finderOptions;
11
+ _hooks = {
12
+ loadUrl: [],
13
+ getFetchClient: [],
14
+ };
15
+ constructor(finderOptions = {}) {
16
+ this._finderOptions = finderOptionsSchema.parse(finderOptions);
17
+ this.loadPlugin(builtInSourcesPlugin);
18
+ for (const plugin of this._finderOptions.plugins) {
19
+ this.loadPlugin(plugin);
20
+ }
21
+ }
22
+ loadPlugin(plugin) {
23
+ if (plugin.sources) {
24
+ this.loadSources(plugin.sources);
25
+ }
26
+ if (plugin.hooks) {
27
+ this.loadHooks(plugin.hooks);
28
+ }
29
+ }
30
+ loadSources(sources) {
31
+ for (const source of sources) {
32
+ this.loadSource(source);
33
+ }
34
+ }
35
+ loadSource(source) {
36
+ if (Object.prototype.hasOwnProperty.call(this.sourceMap, source.id)) {
37
+ console.warn(`Loading "${source.id}" but a source with the same id has already been loaded. The existing source will be overwritten.`);
38
+ }
39
+ // Validate request handlers
40
+ for (const requestHandler of source.requestHandlers) {
41
+ const { paginationType, requestSchema, responses } = requestHandler;
42
+ const errorMessage = `Could not load source "${source.id}" as the request handler "${requestHandler.id}" is invalid`;
43
+ zodParseOrThrow(requestHandlerSchema, requestHandler, { errorMessage });
44
+ const paginationInvalidRequestError = (issue, property) => new Error(`Request handler "${requestHandler.id}" of source "${source.id}" has paginationType ${paginationType} but ${issue === "missing" ? "is missing" : "includes"} ${property} in the request schema.`);
45
+ if (paginationType === "offset") {
46
+ if (!requestSchema.shape.pageNumber) {
47
+ throw paginationInvalidRequestError("missing", "pageNumber");
48
+ }
49
+ if (requestSchema.shape.cursor) {
50
+ throw paginationInvalidRequestError("has", "cursor");
51
+ }
52
+ }
53
+ else if (paginationType === "cursor") {
54
+ if (!requestSchema.shape.cursor) {
55
+ throw paginationInvalidRequestError("missing", "cursor");
56
+ }
57
+ if (requestSchema.shape.pageNumber) {
58
+ throw paginationInvalidRequestError("has", "pageNumber");
59
+ }
60
+ }
61
+ else if (paginationType === "none") {
62
+ if (requestSchema.shape.pageNumber) {
63
+ throw paginationInvalidRequestError("has", "pageNumber");
64
+ }
65
+ if (requestSchema.shape.cursor) {
66
+ throw paginationInvalidRequestError("has", "cursor");
67
+ }
68
+ }
69
+ else {
70
+ throw new Error(`Request handler "${requestHandler.id}" of source "${source.id}" has unsupported paginationType ` +
71
+ `${paginationType}`);
72
+ }
73
+ const nonDefaultResponseDetails = responses.slice(0, -1);
74
+ if (nonDefaultResponseDetails.some((responseDetails) => !responseDetails.requestMatcher)) {
75
+ throw Error(`Some response schema elements are missing "requestMatcher" field for request handler "${requestHandler.id}" of source "${source.id}". ("requestMatcher" is mandatory in all except the last response schema element.`);
76
+ }
77
+ }
78
+ this.sourceMap[source.id] = source;
79
+ }
80
+ loadHooks(hooks) {
81
+ for (const [hookName, hook] of Object.entries(hooks)) {
82
+ if (hook) {
83
+ this._hooks[hookName].push(hook);
84
+ }
85
+ }
86
+ }
87
+ getSource(sourceId) {
88
+ const source = this.sourceMap[sourceId];
89
+ if (!source) {
90
+ throw new Error(`Attempted to query an unknown source. If "${sourceId}" is provided by a plugin please make sure that plugin is loaded first before attempting to query.`);
91
+ }
92
+ return source;
93
+ }
94
+ getRequestHandler(sourceId, queryType) {
95
+ const source = this.getSource(sourceId);
96
+ const handler = source.requestHandlers.find((handler) => handler.id === queryType);
97
+ if (!handler) {
98
+ throw new Error(`The source "${sourceId}" does not provide a request handler for the query type "${queryType}".`);
99
+ }
100
+ return handler;
101
+ }
102
+ getRequestSchema(sourceId, queryType) {
103
+ return this.getRequestHandler(sourceId, queryType).requestSchema;
104
+ }
105
+ }