@headless-lms/adapter-defaults 0.2.0 → 0.3.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.
- package/dist/logging/index.js +12 -1
- package/dist/logging/index.js.map +1 -1
- package/dist/storage/index.d.ts +6 -0
- package/dist/storage/index.js +7 -0
- package/dist/storage/index.js.map +1 -1
- package/dist/webhooks/index.d.ts +15 -2
- package/dist/webhooks/index.js +68 -12
- package/dist/webhooks/index.js.map +1 -1
- package/dist/workflows/index.d.ts +6 -2
- package/dist/workflows/index.js +24 -5
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/schedule.d.ts +9 -0
- package/dist/workflows/schedule.js +89 -0
- package/dist/workflows/schedule.js.map +1 -0
- package/package.json +3 -3
package/dist/logging/index.js
CHANGED
|
@@ -22,12 +22,23 @@ var PinoLogger = class PinoLogger {
|
|
|
22
22
|
return new PinoLogger(this.instance.child(bindings));
|
|
23
23
|
}
|
|
24
24
|
};
|
|
25
|
+
/** The HTTP access-log shape: the route pattern, never the raw path or query (tokens live there). */
|
|
26
|
+
function requestSerializer(req) {
|
|
27
|
+
return {
|
|
28
|
+
method: req.method,
|
|
29
|
+
route: req.routeOptions?.url,
|
|
30
|
+
remoteAddress: req.ip
|
|
31
|
+
};
|
|
32
|
+
}
|
|
25
33
|
/** The process-wide root: JSON lines, Error serialization under `err`.
|
|
26
34
|
* `destination` exists for tests; omitted → stdout. */
|
|
27
35
|
function createRootLogger(level, destination) {
|
|
28
36
|
const instance = pino({
|
|
29
37
|
level,
|
|
30
|
-
serializers: {
|
|
38
|
+
serializers: {
|
|
39
|
+
err: stdSerializers.err,
|
|
40
|
+
req: requestSerializer
|
|
41
|
+
},
|
|
31
42
|
mixin: () => requestLogContext.current()
|
|
32
43
|
}, destination);
|
|
33
44
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/logging/index.ts"],"sourcesContent":["// Logging adapter — implements the core Logger port over pino. One root\n// instance per process: the container creates it, Fastify reuses it via\n// `loggerInstance`, and every component gets a child bound with { name }.\nimport { pino, stdSerializers, type DestinationStream, type Logger as PinoInstance } from \"pino\";\nimport type { Logger } from \"@headless-lms/core/shared/ports\";\nimport { requestLogContext } from \"./request-context.js\";\n\nexport { requestLogContext, type RequestLogContext } from \"./request-context.js\";\nexport type { PinoInstance };\n\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nexport class PinoLogger implements Logger {\n constructor(private readonly instance: PinoInstance) {}\n\n debug(msg: string, meta?: Record<string, unknown>): void {\n this.instance.debug(meta ?? {}, msg);\n }\n info(msg: string, meta?: Record<string, unknown>): void {\n this.instance.info(meta ?? {}, msg);\n }\n warn(msg: string, meta?: Record<string, unknown>): void {\n this.instance.warn(meta ?? {}, msg);\n }\n error(msg: string, meta?: Record<string, unknown>): void {\n this.instance.error(meta ?? {}, msg);\n }\n child(bindings: Record<string, unknown>): Logger {\n return new PinoLogger(this.instance.child(bindings));\n }\n}\n\n/** The process-wide root: JSON lines, Error serialization under `err`.\n * `destination` exists for tests; omitted → stdout. */\nexport function createRootLogger(\n level: LogLevel,\n destination?: DestinationStream,\n): { instance: PinoInstance; logger: Logger } {\n const instance = pino(\n {\n level,\n serializers: { err: stdSerializers.err },\n // Ambient request correlation (reqId/orgId); call-site meta wins on clash.\n mixin: () => requestLogContext.current(),\n },\n destination,\n );\n return { instance, logger: new PinoLogger(instance) };\n}\n"],"mappings":";;;AAYA,IAAa,aAAb,MAAa,WAA6B;CACX;CAA7B,YAAY,UAAyC;EAAxB,KAAA,WAAA;CAAyB;CAEtD,MAAM,KAAa,MAAsC;EACvD,KAAK,SAAS,MAAM,QAAQ,CAAC,GAAG,GAAG;CACrC;CACA,KAAK,KAAa,MAAsC;EACtD,KAAK,SAAS,KAAK,QAAQ,CAAC,GAAG,GAAG;CACpC;CACA,KAAK,KAAa,MAAsC;EACtD,KAAK,SAAS,KAAK,QAAQ,CAAC,GAAG,GAAG;CACpC;CACA,MAAM,KAAa,MAAsC;EACvD,KAAK,SAAS,MAAM,QAAQ,CAAC,GAAG,GAAG;CACrC;CACA,MAAM,UAA2C;EAC/C,OAAO,IAAI,WAAW,KAAK,SAAS,MAAM,QAAQ,CAAC;CACrD;AACF;;;AAIA,SAAgB,iBACd,OACA,aAC4C;CAC5C,MAAM,WAAW,KACf;EACE;EACA,aAAa,
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/logging/index.ts"],"sourcesContent":["// Logging adapter — implements the core Logger port over pino. One root\n// instance per process: the container creates it, Fastify reuses it via\n// `loggerInstance`, and every component gets a child bound with { name }.\nimport { pino, stdSerializers, type DestinationStream, type Logger as PinoInstance } from \"pino\";\nimport type { Logger } from \"@headless-lms/core/shared/ports\";\nimport { requestLogContext } from \"./request-context.js\";\n\nexport { requestLogContext, type RequestLogContext } from \"./request-context.js\";\nexport type { PinoInstance };\n\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nexport class PinoLogger implements Logger {\n constructor(private readonly instance: PinoInstance) {}\n\n debug(msg: string, meta?: Record<string, unknown>): void {\n this.instance.debug(meta ?? {}, msg);\n }\n info(msg: string, meta?: Record<string, unknown>): void {\n this.instance.info(meta ?? {}, msg);\n }\n warn(msg: string, meta?: Record<string, unknown>): void {\n this.instance.warn(meta ?? {}, msg);\n }\n error(msg: string, meta?: Record<string, unknown>): void {\n this.instance.error(meta ?? {}, msg);\n }\n child(bindings: Record<string, unknown>): Logger {\n return new PinoLogger(this.instance.child(bindings));\n }\n}\n\ninterface LoggedRequest {\n method?: string;\n ip?: string;\n routeOptions?: { url?: string };\n}\n\n/** The HTTP access-log shape: the route pattern, never the raw path or query (tokens live there). */\nfunction requestSerializer(req: LoggedRequest): Record<string, unknown> {\n return { method: req.method, route: req.routeOptions?.url, remoteAddress: req.ip };\n}\n\n/** The process-wide root: JSON lines, Error serialization under `err`.\n * `destination` exists for tests; omitted → stdout. */\nexport function createRootLogger(\n level: LogLevel,\n destination?: DestinationStream,\n): { instance: PinoInstance; logger: Logger } {\n const instance = pino(\n {\n level,\n serializers: { err: stdSerializers.err, req: requestSerializer },\n // Ambient request correlation (reqId/orgId); call-site meta wins on clash.\n mixin: () => requestLogContext.current(),\n },\n destination,\n );\n return { instance, logger: new PinoLogger(instance) };\n}\n"],"mappings":";;;AAYA,IAAa,aAAb,MAAa,WAA6B;CACX;CAA7B,YAAY,UAAyC;EAAxB,KAAA,WAAA;CAAyB;CAEtD,MAAM,KAAa,MAAsC;EACvD,KAAK,SAAS,MAAM,QAAQ,CAAC,GAAG,GAAG;CACrC;CACA,KAAK,KAAa,MAAsC;EACtD,KAAK,SAAS,KAAK,QAAQ,CAAC,GAAG,GAAG;CACpC;CACA,KAAK,KAAa,MAAsC;EACtD,KAAK,SAAS,KAAK,QAAQ,CAAC,GAAG,GAAG;CACpC;CACA,MAAM,KAAa,MAAsC;EACvD,KAAK,SAAS,MAAM,QAAQ,CAAC,GAAG,GAAG;CACrC;CACA,MAAM,UAA2C;EAC/C,OAAO,IAAI,WAAW,KAAK,SAAS,MAAM,QAAQ,CAAC;CACrD;AACF;;AASA,SAAS,kBAAkB,KAA6C;CACtE,OAAO;EAAE,QAAQ,IAAI;EAAQ,OAAO,IAAI,cAAc;EAAK,eAAe,IAAI;CAAG;AACnF;;;AAIA,SAAgB,iBACd,OACA,aAC4C;CAC5C,MAAM,WAAW,KACf;EACE;EACA,aAAa;GAAE,KAAK,eAAe;GAAK,KAAK;EAAkB;EAE/D,aAAa,kBAAkB,QAAQ;CACzC,GACA,WACF;CACA,OAAO;EAAE;EAAU,QAAQ,IAAI,WAAW,QAAQ;CAAE;AACtD"}
|
package/dist/storage/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Logger, ObjectStorage, PresignDownloadInput, PresignedUpload, StoredObj
|
|
|
2
2
|
//#region src/storage/index.d.ts
|
|
3
3
|
declare class StorageAdapter implements ObjectStorage {
|
|
4
4
|
private readonly logger;
|
|
5
|
+
readonly uploadExpirySeconds = 0;
|
|
5
6
|
constructor(logger?: Logger);
|
|
6
7
|
presignUpload(_input: {
|
|
7
8
|
key: string;
|
|
@@ -10,6 +11,11 @@ declare class StorageAdapter implements ObjectStorage {
|
|
|
10
11
|
}): Promise<PresignedUpload>;
|
|
11
12
|
presignDownload(_input: PresignDownloadInput): Promise<string>;
|
|
12
13
|
stat(_key: string): Promise<StoredObjectInfo | null>;
|
|
14
|
+
list(_prefix: string): Promise<StoredObjectInfo[]>;
|
|
15
|
+
copy(_input: {
|
|
16
|
+
from: string;
|
|
17
|
+
to: string;
|
|
18
|
+
}): Promise<void>;
|
|
13
19
|
remove(_key: string): Promise<void>;
|
|
14
20
|
private fail;
|
|
15
21
|
}
|
package/dist/storage/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { noopLogger } from "@headless-lms/core/shared/logger";
|
|
|
2
2
|
//#region src/storage/index.ts
|
|
3
3
|
var StorageAdapter = class {
|
|
4
4
|
logger;
|
|
5
|
+
uploadExpirySeconds = 0;
|
|
5
6
|
constructor(logger = noopLogger) {
|
|
6
7
|
this.logger = logger;
|
|
7
8
|
}
|
|
@@ -14,6 +15,12 @@ var StorageAdapter = class {
|
|
|
14
15
|
async stat(_key) {
|
|
15
16
|
return this.fail("stat");
|
|
16
17
|
}
|
|
18
|
+
async list(_prefix) {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
async copy(_input) {
|
|
22
|
+
return this.fail("copy");
|
|
23
|
+
}
|
|
17
24
|
async remove(_key) {
|
|
18
25
|
return this.fail("remove");
|
|
19
26
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/storage/index.ts"],"sourcesContent":["// Default when no ObjectStorage is injected — boots, fails loudly on use.\nimport type {\n Logger,\n ObjectStorage,\n PresignDownloadInput,\n PresignedUpload,\n StoredObjectInfo,\n} from \"@headless-lms/core/shared/ports\";\nimport { noopLogger } from \"@headless-lms/core/shared/logger\";\n\nexport class StorageAdapter implements ObjectStorage {\n constructor(private readonly logger: Logger = noopLogger) {}\n\n async presignUpload(_input: {\n key: string;\n contentType?: string;\n expiresInSeconds?: number;\n }): Promise<PresignedUpload> {\n return this.fail(\"presignUpload\");\n }\n\n async presignDownload(_input: PresignDownloadInput): Promise<string> {\n return this.fail(\"presignDownload\");\n }\n\n async stat(_key: string): Promise<StoredObjectInfo | null> {\n return this.fail(\"stat\");\n }\n\n async remove(_key: string): Promise<void> {\n return this.fail(\"remove\");\n }\n\n private fail(op: string): never {\n this.logger.error(`storage ${op} failed: no storage adapter configured`);\n throw new Error(\"not implemented\");\n }\n}\n"],"mappings":";;AAUA,IAAa,iBAAb,MAAqD;
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/storage/index.ts"],"sourcesContent":["// Default when no ObjectStorage is injected — boots, fails loudly on use.\nimport type {\n Logger,\n ObjectStorage,\n PresignDownloadInput,\n PresignedUpload,\n StoredObjectInfo,\n} from \"@headless-lms/core/shared/ports\";\nimport { noopLogger } from \"@headless-lms/core/shared/logger\";\n\nexport class StorageAdapter implements ObjectStorage {\n readonly uploadExpirySeconds = 0;\n\n constructor(private readonly logger: Logger = noopLogger) {}\n\n async presignUpload(_input: {\n key: string;\n contentType?: string;\n expiresInSeconds?: number;\n }): Promise<PresignedUpload> {\n return this.fail(\"presignUpload\");\n }\n\n async presignDownload(_input: PresignDownloadInput): Promise<string> {\n return this.fail(\"presignDownload\");\n }\n\n async stat(_key: string): Promise<StoredObjectInfo | null> {\n return this.fail(\"stat\");\n }\n\n async list(_prefix: string): Promise<StoredObjectInfo[]> {\n return [];\n }\n\n async copy(_input: { from: string; to: string }): Promise<void> {\n return this.fail(\"copy\");\n }\n\n async remove(_key: string): Promise<void> {\n return this.fail(\"remove\");\n }\n\n private fail(op: string): never {\n this.logger.error(`storage ${op} failed: no storage adapter configured`);\n throw new Error(\"not implemented\");\n }\n}\n"],"mappings":";;AAUA,IAAa,iBAAb,MAAqD;CAGtB;CAF7B,sBAA+B;CAE/B,YAAY,SAAkC,YAAY;EAA7B,KAAA,SAAA;CAA8B;CAE3D,MAAM,cAAc,QAIS;EAC3B,OAAO,KAAK,KAAK,eAAe;CAClC;CAEA,MAAM,gBAAgB,QAA+C;EACnE,OAAO,KAAK,KAAK,iBAAiB;CACpC;CAEA,MAAM,KAAK,MAAgD;EACzD,OAAO,KAAK,KAAK,MAAM;CACzB;CAEA,MAAM,KAAK,SAA8C;EACvD,OAAO,CAAC;CACV;CAEA,MAAM,KAAK,QAAqD;EAC9D,OAAO,KAAK,KAAK,MAAM;CACzB;CAEA,MAAM,OAAO,MAA6B;EACxC,OAAO,KAAK,KAAK,QAAQ;CAC3B;CAEA,KAAa,IAAmB;EAC9B,KAAK,OAAO,MAAM,WAAW,GAAG,uCAAuC;EACvE,MAAM,IAAI,MAAM,iBAAiB;CACnC;AACF"}
|
package/dist/webhooks/index.d.ts
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
import { WebhookRequest, WebhookResponse, WebhookSender } from "@headless-lms/core/types";
|
|
2
2
|
//#region src/webhooks/index.d.ts
|
|
3
|
+
interface FetchWebhookSenderOptions {
|
|
4
|
+
timeoutMs?: number;
|
|
5
|
+
/** Deliver to loopback and private network addresses too. Local development and testing only. */
|
|
6
|
+
allowPrivateNetwork?: boolean;
|
|
7
|
+
}
|
|
8
|
+
declare class WebhookAddressRefusedError extends Error {
|
|
9
|
+
readonly host: string;
|
|
10
|
+
readonly address: string;
|
|
11
|
+
constructor(host: string, address: string);
|
|
12
|
+
}
|
|
13
|
+
/** POSTs each delivery to the address its host resolved to once, so DNS can't swap it after the check. */
|
|
3
14
|
declare class FetchWebhookSender implements WebhookSender {
|
|
4
15
|
private readonly timeoutMs;
|
|
5
|
-
|
|
16
|
+
private readonly allowPrivateNetwork;
|
|
17
|
+
constructor(options?: FetchWebhookSenderOptions);
|
|
6
18
|
send(request: WebhookRequest): Promise<WebhookResponse>;
|
|
19
|
+
private resolve;
|
|
7
20
|
}
|
|
8
21
|
//#endregion
|
|
9
|
-
export { FetchWebhookSender };
|
|
22
|
+
export { FetchWebhookSender, FetchWebhookSenderOptions, WebhookAddressRefusedError };
|
|
10
23
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/webhooks/index.js
CHANGED
|
@@ -1,23 +1,79 @@
|
|
|
1
|
+
import { lookup } from "node:dns/promises";
|
|
2
|
+
import { request } from "node:http";
|
|
3
|
+
import { request as request$1 } from "node:https";
|
|
4
|
+
import { isIP } from "node:net";
|
|
5
|
+
import { isPublicIp } from "@headless-lms/core/shared/network";
|
|
1
6
|
//#region src/webhooks/index.ts
|
|
2
7
|
const DEFAULT_TIMEOUT_MS = 1e4;
|
|
8
|
+
var WebhookAddressRefusedError = class extends Error {
|
|
9
|
+
host;
|
|
10
|
+
address;
|
|
11
|
+
constructor(host, address) {
|
|
12
|
+
super(`webhook host "${host}" resolves to ${address}, which is not a public address`);
|
|
13
|
+
this.host = host;
|
|
14
|
+
this.address = address;
|
|
15
|
+
this.name = "WebhookAddressRefusedError";
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
/** POSTs each delivery to the address its host resolved to once, so DNS can't swap it after the check. */
|
|
3
19
|
var FetchWebhookSender = class {
|
|
4
20
|
timeoutMs;
|
|
5
|
-
|
|
6
|
-
|
|
21
|
+
allowPrivateNetwork;
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
24
|
+
this.allowPrivateNetwork = options.allowPrivateNetwork ?? false;
|
|
7
25
|
}
|
|
8
26
|
async send(request) {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
27
|
+
const url = new URL(request.url);
|
|
28
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") throw new Error(`webhook URL protocol "${url.protocol}" is not supported`);
|
|
29
|
+
const signal = AbortSignal.timeout(this.timeoutMs);
|
|
30
|
+
const addresses = await this.resolve(url.hostname.replace(/^\[(.*)\]$/, "$1"));
|
|
31
|
+
signal.throwIfAborted();
|
|
32
|
+
return post(url, request, addresses, signal);
|
|
33
|
+
}
|
|
34
|
+
async resolve(host) {
|
|
35
|
+
const family = isIP(host);
|
|
36
|
+
const addresses = family === 0 ? await lookup(host, {
|
|
37
|
+
all: true,
|
|
38
|
+
verbatim: true
|
|
39
|
+
}) : [{
|
|
40
|
+
address: host,
|
|
41
|
+
family
|
|
42
|
+
}];
|
|
43
|
+
if (addresses.length === 0) throw new Error(`webhook host "${host}" did not resolve`);
|
|
44
|
+
if (!this.allowPrivateNetwork) {
|
|
45
|
+
const refused = addresses.find(({ address }) => !isPublicIp(address));
|
|
46
|
+
if (refused) throw new WebhookAddressRefusedError(host, refused.address);
|
|
47
|
+
}
|
|
48
|
+
return addresses;
|
|
18
49
|
}
|
|
19
50
|
};
|
|
51
|
+
function post(url, request$2, addresses, signal) {
|
|
52
|
+
const pinned = (_hostname, options, callback) => {
|
|
53
|
+
const [first] = addresses;
|
|
54
|
+
if (options.all) callback(null, addresses);
|
|
55
|
+
else callback(null, first.address, first.family);
|
|
56
|
+
};
|
|
57
|
+
const options = {
|
|
58
|
+
method: "POST",
|
|
59
|
+
headers: {
|
|
60
|
+
...request$2.headers,
|
|
61
|
+
"content-length": Buffer.byteLength(request$2.body)
|
|
62
|
+
},
|
|
63
|
+
lookup: pinned,
|
|
64
|
+
signal
|
|
65
|
+
};
|
|
66
|
+
const send = url.protocol === "https:" ? request$1 : request;
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
const req = send(url, options, (res) => {
|
|
69
|
+
resolve({ status: res.statusCode ?? 0 });
|
|
70
|
+
res.destroy();
|
|
71
|
+
});
|
|
72
|
+
req.on("error", reject);
|
|
73
|
+
req.end(request$2.body);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
20
76
|
//#endregion
|
|
21
|
-
export { FetchWebhookSender };
|
|
77
|
+
export { FetchWebhookSender, WebhookAddressRefusedError };
|
|
22
78
|
|
|
23
79
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/webhooks/index.ts"],"sourcesContent":["import type { WebhookRequest, WebhookResponse, WebhookSender } from \"@headless-lms/core/types\";\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nexport class FetchWebhookSender implements WebhookSender {\n
|
|
1
|
+
{"version":3,"file":"index.js","names":["request","httpsRequest","httpRequest"],"sources":["../../src/webhooks/index.ts"],"sourcesContent":["import { lookup } from \"node:dns/promises\";\nimport { request as httpRequest, type RequestOptions } from \"node:http\";\nimport { request as httpsRequest } from \"node:https\";\nimport { isIP, type LookupFunction } from \"node:net\";\nimport { isPublicIp } from \"@headless-lms/core/shared/network\";\nimport type { WebhookRequest, WebhookResponse, WebhookSender } from \"@headless-lms/core/types\";\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nexport interface FetchWebhookSenderOptions {\n timeoutMs?: number;\n /** Deliver to loopback and private network addresses too. Local development and testing only. */\n allowPrivateNetwork?: boolean;\n}\n\ninterface ResolvedAddress {\n address: string;\n family: number;\n}\n\nexport class WebhookAddressRefusedError extends Error {\n constructor(\n readonly host: string,\n readonly address: string,\n ) {\n super(`webhook host \"${host}\" resolves to ${address}, which is not a public address`);\n this.name = \"WebhookAddressRefusedError\";\n }\n}\n\n/** POSTs each delivery to the address its host resolved to once, so DNS can't swap it after the check. */\nexport class FetchWebhookSender implements WebhookSender {\n private readonly timeoutMs: number;\n private readonly allowPrivateNetwork: boolean;\n\n constructor(options: FetchWebhookSenderOptions = {}) {\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.allowPrivateNetwork = options.allowPrivateNetwork ?? false;\n }\n\n async send(request: WebhookRequest): Promise<WebhookResponse> {\n const url = new URL(request.url);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw new Error(`webhook URL protocol \"${url.protocol}\" is not supported`);\n }\n const signal = AbortSignal.timeout(this.timeoutMs);\n const addresses = await this.resolve(url.hostname.replace(/^\\[(.*)\\]$/, \"$1\"));\n signal.throwIfAborted();\n return post(url, request, addresses, signal);\n }\n\n private async resolve(host: string): Promise<ResolvedAddress[]> {\n const family = isIP(host);\n const addresses =\n family === 0\n ? await lookup(host, { all: true, verbatim: true })\n : [{ address: host, family }];\n if (addresses.length === 0) {\n throw new Error(`webhook host \"${host}\" did not resolve`);\n }\n if (!this.allowPrivateNetwork) {\n const refused = addresses.find(({ address }) => !isPublicIp(address));\n if (refused) {\n throw new WebhookAddressRefusedError(host, refused.address);\n }\n }\n return addresses;\n }\n}\n\nfunction post(\n url: URL,\n request: WebhookRequest,\n addresses: ResolvedAddress[],\n signal: AbortSignal,\n): Promise<WebhookResponse> {\n const pinned: LookupFunction = (_hostname, options, callback) => {\n const [first] = addresses;\n if (options.all) {\n callback(null, addresses);\n } else {\n callback(null, first!.address, first!.family);\n }\n };\n const options: RequestOptions = {\n method: \"POST\",\n headers: { ...request.headers, \"content-length\": Buffer.byteLength(request.body) },\n lookup: pinned,\n signal,\n };\n const send = url.protocol === \"https:\" ? httpsRequest : httpRequest;\n return new Promise((resolve, reject) => {\n const req = send(url, options, (res) => {\n resolve({ status: res.statusCode ?? 0 });\n res.destroy();\n });\n req.on(\"error\", reject);\n req.end(request.body);\n });\n}\n"],"mappings":";;;;;;AAOA,MAAM,qBAAqB;AAa3B,IAAa,6BAAb,cAAgD,MAAM;CAEzC;CACA;CAFX,YACE,MACA,SACA;EACA,MAAM,iBAAiB,KAAK,gBAAgB,QAAQ,gCAAgC;EAH3E,KAAA,OAAA;EACA,KAAA,UAAA;EAGT,KAAK,OAAO;CACd;AACF;;AAGA,IAAa,qBAAb,MAAyD;CACvD;CACA;CAEA,YAAY,UAAqC,CAAC,GAAG;EACnD,KAAK,YAAY,QAAQ,aAAa;EACtC,KAAK,sBAAsB,QAAQ,uBAAuB;CAC5D;CAEA,MAAM,KAAK,SAAmD;EAC5D,MAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;EAC/B,IAAI,IAAI,aAAa,WAAW,IAAI,aAAa,UAC/C,MAAM,IAAI,MAAM,yBAAyB,IAAI,SAAS,mBAAmB;EAE3E,MAAM,SAAS,YAAY,QAAQ,KAAK,SAAS;EACjD,MAAM,YAAY,MAAM,KAAK,QAAQ,IAAI,SAAS,QAAQ,cAAc,IAAI,CAAC;EAC7E,OAAO,eAAe;EACtB,OAAO,KAAK,KAAK,SAAS,WAAW,MAAM;CAC7C;CAEA,MAAc,QAAQ,MAA0C;EAC9D,MAAM,SAAS,KAAK,IAAI;EACxB,MAAM,YACJ,WAAW,IACP,MAAM,OAAO,MAAM;GAAE,KAAK;GAAM,UAAU;EAAK,CAAC,IAChD,CAAC;GAAE,SAAS;GAAM;EAAO,CAAC;EAChC,IAAI,UAAU,WAAW,GACvB,MAAM,IAAI,MAAM,iBAAiB,KAAK,kBAAkB;EAE1D,IAAI,CAAC,KAAK,qBAAqB;GAC7B,MAAM,UAAU,UAAU,MAAM,EAAE,cAAc,CAAC,WAAW,OAAO,CAAC;GACpE,IAAI,SACF,MAAM,IAAI,2BAA2B,MAAM,QAAQ,OAAO;EAE9D;EACA,OAAO;CACT;AACF;AAEA,SAAS,KACP,KACA,WACA,WACA,QAC0B;CAC1B,MAAM,UAA0B,WAAW,SAAS,aAAa;EAC/D,MAAM,CAAC,SAAS;EAChB,IAAI,QAAQ,KACV,SAAS,MAAM,SAAS;OAExB,SAAS,MAAM,MAAO,SAAS,MAAO,MAAM;CAEhD;CACA,MAAM,UAA0B;EAC9B,QAAQ;EACR,SAAS;GAAE,GAAGA,UAAQ;GAAS,kBAAkB,OAAO,WAAWA,UAAQ,IAAI;EAAE;EACjF,QAAQ;EACR;CACF;CACA,MAAM,OAAO,IAAI,aAAa,WAAWC,YAAeC;CACxD,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,MAAM,KAAK,KAAK,UAAU,QAAQ;GACtC,QAAQ,EAAE,QAAQ,IAAI,cAAc,EAAE,CAAC;GACvC,IAAI,QAAQ;EACd,CAAC;EACD,IAAI,GAAG,SAAS,MAAM;EACtB,IAAI,IAAIF,UAAQ,IAAI;CACtB,CAAC;AACH"}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { startScheduleLoop } from "./schedule.js";
|
|
2
|
+
import { AutomationDispatch, AutomationEngine, AutomationExecutor, Logger } from "@headless-lms/core/types";
|
|
2
3
|
//#region src/workflows/index.d.ts
|
|
3
4
|
declare class InlineAutomationEngine implements AutomationEngine {
|
|
5
|
+
private readonly logger?;
|
|
4
6
|
private executor;
|
|
7
|
+
private schedule;
|
|
8
|
+
constructor(logger?: Logger | undefined);
|
|
5
9
|
register(executor: AutomationExecutor): void;
|
|
6
10
|
dispatch(d: AutomationDispatch): Promise<void>;
|
|
7
11
|
start(): Promise<void>;
|
|
8
12
|
stop(): Promise<void>;
|
|
9
13
|
}
|
|
10
14
|
//#endregion
|
|
11
|
-
export { InlineAutomationEngine };
|
|
15
|
+
export { InlineAutomationEngine, startScheduleLoop };
|
|
12
16
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/workflows/index.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
import { startScheduleLoop } from "./schedule.js";
|
|
2
|
+
import { ActionFailedError, actionFailureMessage } from "@headless-lms/core/automations";
|
|
1
3
|
//#region src/workflows/index.ts
|
|
2
4
|
var InlineAutomationEngine = class {
|
|
5
|
+
logger;
|
|
3
6
|
executor;
|
|
7
|
+
schedule;
|
|
8
|
+
constructor(logger) {
|
|
9
|
+
this.logger = logger;
|
|
10
|
+
}
|
|
4
11
|
register(executor) {
|
|
5
12
|
this.executor = executor;
|
|
6
13
|
}
|
|
@@ -11,21 +18,33 @@ var InlineAutomationEngine = class {
|
|
|
11
18
|
for (let index = 0; index < d.actions.length; index++) try {
|
|
12
19
|
results.push(await executor.runAction(d, index));
|
|
13
20
|
} catch (err) {
|
|
14
|
-
|
|
21
|
+
if (!(err instanceof ActionFailedError)) this.logger?.error("automation action failed", {
|
|
22
|
+
orgId: d.orgId,
|
|
23
|
+
automationId: d.automationId,
|
|
24
|
+
runId: d.runId,
|
|
25
|
+
index,
|
|
26
|
+
err
|
|
27
|
+
});
|
|
15
28
|
results.push({
|
|
16
29
|
index,
|
|
17
30
|
type: d.actions[index].type,
|
|
18
31
|
status: "failed",
|
|
19
|
-
error:
|
|
32
|
+
error: actionFailureMessage(err)
|
|
20
33
|
});
|
|
21
34
|
break;
|
|
22
35
|
}
|
|
23
36
|
await executor.finalize(d, results);
|
|
24
37
|
}
|
|
25
|
-
async start() {
|
|
26
|
-
|
|
38
|
+
async start() {
|
|
39
|
+
this.schedule?.stop();
|
|
40
|
+
this.schedule = startScheduleLoop(() => this.executor, this.logger);
|
|
41
|
+
}
|
|
42
|
+
async stop() {
|
|
43
|
+
this.schedule?.stop();
|
|
44
|
+
this.schedule = void 0;
|
|
45
|
+
}
|
|
27
46
|
};
|
|
28
47
|
//#endregion
|
|
29
|
-
export { InlineAutomationEngine };
|
|
48
|
+
export { InlineAutomationEngine, startScheduleLoop };
|
|
30
49
|
|
|
31
50
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/workflows/index.ts"],"sourcesContent":["// Default in-process engine — runs a dispatched automation immediately, one\n// attempt per action; durable engines (Hatchet, …) are installation adapters.\nimport type {\n AutomationActionResult,\n AutomationDispatch,\n AutomationEngine,\n AutomationExecutor,\n} from \"@headless-lms/core/types\";\n\nexport class InlineAutomationEngine implements AutomationEngine {\n private executor: AutomationExecutor | undefined;\n\n register(executor: AutomationExecutor): void {\n this.executor = executor;\n }\n\n async dispatch(d: AutomationDispatch): Promise<void> {\n if (!this.executor) {\n throw new Error(\"InlineAutomationEngine.dispatch called before register()\");\n }\n const executor = this.executor;\n\n const results: AutomationActionResult[] = [];\n for (let index = 0; index < d.actions.length; index++) {\n try {\n results.push(await executor.runAction(d, index));\n } catch (err) {\n
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/workflows/index.ts"],"sourcesContent":["// Default in-process engine — runs a dispatched automation immediately, one\n// attempt per action; durable engines (Hatchet, …) are installation adapters.\nimport type {\n AutomationActionResult,\n AutomationDispatch,\n AutomationEngine,\n AutomationExecutor,\n Logger,\n} from \"@headless-lms/core/types\";\nimport { ActionFailedError, actionFailureMessage } from \"@headless-lms/core/automations\";\nimport { startScheduleLoop } from \"./schedule.js\";\n\nexport { startScheduleLoop };\n\nexport class InlineAutomationEngine implements AutomationEngine {\n private executor: AutomationExecutor | undefined;\n private schedule: { stop(): void } | undefined;\n\n constructor(private readonly logger?: Logger) {}\n\n register(executor: AutomationExecutor): void {\n this.executor = executor;\n }\n\n async dispatch(d: AutomationDispatch): Promise<void> {\n if (!this.executor) {\n throw new Error(\"InlineAutomationEngine.dispatch called before register()\");\n }\n const executor = this.executor;\n\n const results: AutomationActionResult[] = [];\n for (let index = 0; index < d.actions.length; index++) {\n try {\n results.push(await executor.runAction(d, index));\n } catch (err) {\n if (!(err instanceof ActionFailedError)) {\n this.logger?.error(\"automation action failed\", {\n orgId: d.orgId,\n automationId: d.automationId,\n runId: d.runId,\n index,\n err,\n });\n }\n results.push({\n index,\n type: d.actions[index]!.type,\n status: \"failed\",\n error: actionFailureMessage(err),\n });\n break;\n }\n }\n\n await executor.finalize(d, results);\n }\n\n async start(): Promise<void> {\n this.schedule?.stop();\n this.schedule = startScheduleLoop(() => this.executor, this.logger);\n }\n\n async stop(): Promise<void> {\n this.schedule?.stop();\n this.schedule = undefined;\n }\n}\n"],"mappings":";;;AAcA,IAAa,yBAAb,MAAgE;CAIjC;CAH7B;CACA;CAEA,YAAY,QAAkC;EAAjB,KAAA,SAAA;CAAkB;CAE/C,SAAS,UAAoC;EAC3C,KAAK,WAAW;CAClB;CAEA,MAAM,SAAS,GAAsC;EACnD,IAAI,CAAC,KAAK,UACR,MAAM,IAAI,MAAM,0DAA0D;EAE5E,MAAM,WAAW,KAAK;EAEtB,MAAM,UAAoC,CAAC;EAC3C,KAAK,IAAI,QAAQ,GAAG,QAAQ,EAAE,QAAQ,QAAQ,SAC5C,IAAI;GACF,QAAQ,KAAK,MAAM,SAAS,UAAU,GAAG,KAAK,CAAC;EACjD,SAAS,KAAK;GACZ,IAAI,EAAE,eAAe,oBACnB,KAAK,QAAQ,MAAM,4BAA4B;IAC7C,OAAO,EAAE;IACT,cAAc,EAAE;IAChB,OAAO,EAAE;IACT;IACA;GACF,CAAC;GAEH,QAAQ,KAAK;IACX;IACA,MAAM,EAAE,QAAQ,MAAM,CAAE;IACxB,QAAQ;IACR,OAAO,qBAAqB,GAAG;GACjC,CAAC;GACD;EACF;EAGF,MAAM,SAAS,SAAS,GAAG,OAAO;CACpC;CAEA,MAAM,QAAuB;EAC3B,KAAK,UAAU,KAAK;EACpB,KAAK,WAAW,wBAAwB,KAAK,UAAU,KAAK,MAAM;CACpE;CAEA,MAAM,OAAsB;EAC1B,KAAK,UAAU,KAAK;EACpB,KAAK,WAAW,KAAA;CAClB;AACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { AutomationExecutor, Logger } from "@headless-lms/core/types";
|
|
2
|
+
//#region src/workflows/schedule.d.ts
|
|
3
|
+
declare function fireDueSchedules(executor: AutomationExecutor, now: Date, lastFired: Map<string, number>, logger?: Logger): Promise<boolean>;
|
|
4
|
+
declare function startScheduleLoop(getExecutor: () => AutomationExecutor | undefined, logger?: Logger): {
|
|
5
|
+
stop(): void;
|
|
6
|
+
};
|
|
7
|
+
//#endregion
|
|
8
|
+
export { fireDueSchedules, startScheduleLoop };
|
|
9
|
+
//# sourceMappingURL=schedule.d.ts.map
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { noopLogger } from "@headless-lms/core/shared/logger";
|
|
2
|
+
import { cronMatches } from "@headless-lms/core/automations";
|
|
3
|
+
//#region src/workflows/schedule.ts
|
|
4
|
+
const MINUTE_MS = 6e4;
|
|
5
|
+
function floorUtcMinute(at) {
|
|
6
|
+
const copy = new Date(at);
|
|
7
|
+
copy.setUTCSeconds(0, 0);
|
|
8
|
+
return copy.getTime();
|
|
9
|
+
}
|
|
10
|
+
function scheduleKey(schedule) {
|
|
11
|
+
return `${schedule.orgId}\0${schedule.automationId}\0${schedule.cron}`;
|
|
12
|
+
}
|
|
13
|
+
async function fireDueSchedules(executor, now, lastFired, logger = noopLogger) {
|
|
14
|
+
if (!executor.listSchedules || !executor.fireSchedule) return false;
|
|
15
|
+
const nowMs = floorUtcMinute(now);
|
|
16
|
+
let schedules = [];
|
|
17
|
+
try {
|
|
18
|
+
schedules = await executor.listSchedules();
|
|
19
|
+
} catch (err) {
|
|
20
|
+
logger.error("automation schedules not listed", { err });
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24
|
+
for (const schedule of schedules) {
|
|
25
|
+
const key = scheduleKey(schedule);
|
|
26
|
+
seen.add(key);
|
|
27
|
+
const previous = lastFired.get(key);
|
|
28
|
+
const start = previous == null ? nowMs : previous + MINUTE_MS;
|
|
29
|
+
for (let minute = start; minute <= nowMs; minute += MINUTE_MS) {
|
|
30
|
+
const at = new Date(minute);
|
|
31
|
+
const context = {
|
|
32
|
+
orgId: schedule.orgId,
|
|
33
|
+
automationId: schedule.automationId,
|
|
34
|
+
cron: schedule.cron,
|
|
35
|
+
at: at.toISOString()
|
|
36
|
+
};
|
|
37
|
+
let due;
|
|
38
|
+
try {
|
|
39
|
+
due = cronMatches(schedule.cron, at);
|
|
40
|
+
} catch (err) {
|
|
41
|
+
logger.error("automation schedule not checked", {
|
|
42
|
+
...context,
|
|
43
|
+
err
|
|
44
|
+
});
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
if (due) try {
|
|
48
|
+
await executor.fireSchedule(schedule.orgId, schedule.automationId, at);
|
|
49
|
+
} catch (err) {
|
|
50
|
+
logger.error("automation schedule not fired", {
|
|
51
|
+
...context,
|
|
52
|
+
err
|
|
53
|
+
});
|
|
54
|
+
if (!lastFired.has(key)) lastFired.set(key, minute - MINUTE_MS);
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
lastFired.set(key, minute);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
for (const key of lastFired.keys()) if (!seen.has(key)) lastFired.delete(key);
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
function startScheduleLoop(getExecutor, logger = noopLogger) {
|
|
64
|
+
const lastFired = /* @__PURE__ */ new Map();
|
|
65
|
+
let timer;
|
|
66
|
+
let running = false;
|
|
67
|
+
const tick = () => {
|
|
68
|
+
if (running) return;
|
|
69
|
+
const executor = getExecutor();
|
|
70
|
+
if (!executor) return;
|
|
71
|
+
running = true;
|
|
72
|
+
fireDueSchedules(executor, /* @__PURE__ */ new Date(), lastFired, logger).catch((err) => {
|
|
73
|
+
logger.error("automation schedule tick failed", { err });
|
|
74
|
+
}).finally(() => {
|
|
75
|
+
running = false;
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
tick();
|
|
79
|
+
timer = setInterval(tick, MINUTE_MS);
|
|
80
|
+
timer.unref?.();
|
|
81
|
+
return { stop() {
|
|
82
|
+
if (timer) clearInterval(timer);
|
|
83
|
+
timer = void 0;
|
|
84
|
+
} };
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
export { fireDueSchedules, startScheduleLoop };
|
|
88
|
+
|
|
89
|
+
//# sourceMappingURL=schedule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule.js","names":[],"sources":["../../src/workflows/schedule.ts"],"sourcesContent":["import type { AutomationExecutor, Logger, ScheduledAutomation } from \"@headless-lms/core/types\";\nimport { cronMatches } from \"@headless-lms/core/automations\";\nimport { noopLogger } from \"@headless-lms/core/shared/logger\";\n\nconst MINUTE_MS = 60_000;\n\nfunction floorUtcMinute(at: Date): number {\n const copy = new Date(at);\n copy.setUTCSeconds(0, 0);\n return copy.getTime();\n}\n\nfunction scheduleKey(schedule: ScheduledAutomation): string {\n return `${schedule.orgId}\\0${schedule.automationId}\\0${schedule.cron}`;\n}\n\nexport async function fireDueSchedules(\n executor: AutomationExecutor,\n now: Date,\n lastFired: Map<string, number>,\n logger: Logger = noopLogger,\n): Promise<boolean> {\n if (!executor.listSchedules || !executor.fireSchedule) {\n return false;\n }\n const nowMs = floorUtcMinute(now);\n let schedules: ScheduledAutomation[] = [];\n try {\n schedules = await executor.listSchedules();\n } catch (err) {\n logger.error(\"automation schedules not listed\", { err });\n return false;\n }\n const seen = new Set<string>();\n for (const schedule of schedules) {\n const key = scheduleKey(schedule);\n seen.add(key);\n const previous = lastFired.get(key);\n const start = previous == null ? nowMs : previous + MINUTE_MS;\n for (let minute = start; minute <= nowMs; minute += MINUTE_MS) {\n const at = new Date(minute);\n const context = {\n orgId: schedule.orgId,\n automationId: schedule.automationId,\n cron: schedule.cron,\n at: at.toISOString(),\n };\n let due: boolean;\n try {\n due = cronMatches(schedule.cron, at);\n } catch (err) {\n logger.error(\"automation schedule not checked\", { ...context, err });\n break;\n }\n if (due) {\n try {\n await executor.fireSchedule(schedule.orgId, schedule.automationId, at);\n } catch (err) {\n logger.error(\"automation schedule not fired\", { ...context, err });\n if (!lastFired.has(key)) {\n lastFired.set(key, minute - MINUTE_MS);\n }\n break;\n }\n }\n lastFired.set(key, minute);\n }\n }\n for (const key of lastFired.keys()) {\n if (!seen.has(key)) {\n lastFired.delete(key);\n }\n }\n return true;\n}\n\nexport function startScheduleLoop(\n getExecutor: () => AutomationExecutor | undefined,\n logger: Logger = noopLogger,\n): {\n stop(): void;\n} {\n const lastFired = new Map<string, number>();\n let timer: ReturnType<typeof setInterval> | undefined;\n let running = false;\n const tick = () => {\n if (running) {\n return;\n }\n const executor = getExecutor();\n if (!executor) {\n return;\n }\n running = true;\n void fireDueSchedules(executor, new Date(), lastFired, logger)\n .catch((err: unknown) => {\n logger.error(\"automation schedule tick failed\", { err });\n })\n .finally(() => {\n running = false;\n });\n };\n tick();\n timer = setInterval(tick, MINUTE_MS);\n timer.unref?.();\n return {\n stop() {\n if (timer) {\n clearInterval(timer);\n }\n timer = undefined;\n },\n };\n}\n"],"mappings":";;;AAIA,MAAM,YAAY;AAElB,SAAS,eAAe,IAAkB;CACxC,MAAM,OAAO,IAAI,KAAK,EAAE;CACxB,KAAK,cAAc,GAAG,CAAC;CACvB,OAAO,KAAK,QAAQ;AACtB;AAEA,SAAS,YAAY,UAAuC;CAC1D,OAAO,GAAG,SAAS,MAAM,IAAI,SAAS,aAAa,IAAI,SAAS;AAClE;AAEA,eAAsB,iBACpB,UACA,KACA,WACA,SAAiB,YACC;CAClB,IAAI,CAAC,SAAS,iBAAiB,CAAC,SAAS,cACvC,OAAO;CAET,MAAM,QAAQ,eAAe,GAAG;CAChC,IAAI,YAAmC,CAAC;CACxC,IAAI;EACF,YAAY,MAAM,SAAS,cAAc;CAC3C,SAAS,KAAK;EACZ,OAAO,MAAM,mCAAmC,EAAE,IAAI,CAAC;EACvD,OAAO;CACT;CACA,MAAM,uBAAO,IAAI,IAAY;CAC7B,KAAK,MAAM,YAAY,WAAW;EAChC,MAAM,MAAM,YAAY,QAAQ;EAChC,KAAK,IAAI,GAAG;EACZ,MAAM,WAAW,UAAU,IAAI,GAAG;EAClC,MAAM,QAAQ,YAAY,OAAO,QAAQ,WAAW;EACpD,KAAK,IAAI,SAAS,OAAO,UAAU,OAAO,UAAU,WAAW;GAC7D,MAAM,KAAK,IAAI,KAAK,MAAM;GAC1B,MAAM,UAAU;IACd,OAAO,SAAS;IAChB,cAAc,SAAS;IACvB,MAAM,SAAS;IACf,IAAI,GAAG,YAAY;GACrB;GACA,IAAI;GACJ,IAAI;IACF,MAAM,YAAY,SAAS,MAAM,EAAE;GACrC,SAAS,KAAK;IACZ,OAAO,MAAM,mCAAmC;KAAE,GAAG;KAAS;IAAI,CAAC;IACnE;GACF;GACA,IAAI,KACF,IAAI;IACF,MAAM,SAAS,aAAa,SAAS,OAAO,SAAS,cAAc,EAAE;GACvE,SAAS,KAAK;IACZ,OAAO,MAAM,iCAAiC;KAAE,GAAG;KAAS;IAAI,CAAC;IACjE,IAAI,CAAC,UAAU,IAAI,GAAG,GACpB,UAAU,IAAI,KAAK,SAAS,SAAS;IAEvC;GACF;GAEF,UAAU,IAAI,KAAK,MAAM;EAC3B;CACF;CACA,KAAK,MAAM,OAAO,UAAU,KAAK,GAC/B,IAAI,CAAC,KAAK,IAAI,GAAG,GACf,UAAU,OAAO,GAAG;CAGxB,OAAO;AACT;AAEA,SAAgB,kBACd,aACA,SAAiB,YAGjB;CACA,MAAM,4BAAY,IAAI,IAAoB;CAC1C,IAAI;CACJ,IAAI,UAAU;CACd,MAAM,aAAa;EACjB,IAAI,SACF;EAEF,MAAM,WAAW,YAAY;EAC7B,IAAI,CAAC,UACH;EAEF,UAAU;EACV,iBAAsB,0BAAU,IAAI,KAAK,GAAG,WAAW,MAAM,CAAC,CAC3D,OAAO,QAAiB;GACvB,OAAO,MAAM,mCAAmC,EAAE,IAAI,CAAC;EACzD,CAAC,CAAC,CACD,cAAc;GACb,UAAU;EACZ,CAAC;CACL;CACA,KAAK;CACL,QAAQ,YAAY,MAAM,SAAS;CACnC,MAAM,QAAQ;CACd,OAAO,EACL,OAAO;EACL,IAAI,OACF,cAAc,KAAK;EAErB,QAAQ,KAAA;CACV,EACF;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@headless-lms/adapter-defaults",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./email": {
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"pino": "^10.3.1",
|
|
44
|
-
"@headless-lms/adapter-db": "0.
|
|
45
|
-
"@headless-lms/core": "0.
|
|
44
|
+
"@headless-lms/adapter-db": "0.3.0",
|
|
45
|
+
"@headless-lms/core": "0.3.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/node": "^22.10.2",
|