@headless-lms/adapter-defaults 0.1.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/LICENSE +21 -0
- package/dist/email/index.d.ts +15 -0
- package/dist/email/index.js +26 -0
- package/dist/email/index.js.map +1 -0
- package/dist/events/index.d.ts +13 -0
- package/dist/events/index.js +23 -0
- package/dist/events/index.js.map +1 -0
- package/dist/events/outbox-relay.d.ts +38 -0
- package/dist/events/outbox-relay.js +92 -0
- package/dist/events/outbox-relay.js.map +1 -0
- package/dist/logging/index.d.ts +23 -0
- package/dist/logging/index.js +41 -0
- package/dist/logging/index.js.map +1 -0
- package/dist/logging/request-context.d.ts +13 -0
- package/dist/logging/request-context.js +19 -0
- package/dist/logging/request-context.js.map +1 -0
- package/dist/storage/index.d.ts +18 -0
- package/dist/storage/index.js +28 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/workflows/index.d.ts +12 -0
- package/dist/workflows/index.js +31 -0
- package/dist/workflows/index.js.map +1 -0
- package/package.json +68 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 mdwt
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { EmailMessage, EmailSender, EmailTemplateId, Logger, TemplateRenderer } from "@headless-lms/core/shared/ports";
|
|
2
|
+
//#region src/email/index.d.ts
|
|
3
|
+
declare class EmailAdapter implements EmailSender {
|
|
4
|
+
private readonly logger;
|
|
5
|
+
constructor(logger?: Logger);
|
|
6
|
+
send(_message: EmailMessage): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
declare class StubTemplateRenderer implements TemplateRenderer {
|
|
9
|
+
private readonly logger;
|
|
10
|
+
constructor(logger?: Logger);
|
|
11
|
+
render(id: EmailTemplateId): Promise<never>;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { EmailAdapter, StubTemplateRenderer };
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { noopLogger } from "@headless-lms/core/shared/logger";
|
|
2
|
+
//#region src/email/index.ts
|
|
3
|
+
var EmailAdapter = class {
|
|
4
|
+
logger;
|
|
5
|
+
constructor(logger = noopLogger) {
|
|
6
|
+
this.logger = logger;
|
|
7
|
+
}
|
|
8
|
+
async send(_message) {
|
|
9
|
+
this.logger.error("email send failed: no transport configured");
|
|
10
|
+
throw new Error("not implemented");
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
var StubTemplateRenderer = class {
|
|
14
|
+
logger;
|
|
15
|
+
constructor(logger = noopLogger) {
|
|
16
|
+
this.logger = logger;
|
|
17
|
+
}
|
|
18
|
+
async render(id) {
|
|
19
|
+
this.logger.error("template render failed: no renderer configured", { id });
|
|
20
|
+
throw new Error("no template renderer configured");
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
//#endregion
|
|
24
|
+
export { EmailAdapter, StubTemplateRenderer };
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/email/index.ts"],"sourcesContent":["// Default when no EmailSender is injected — fails loudly on use.\nimport type { EmailSender, EmailMessage, EmailTemplateId, Logger, TemplateRenderer } from '@headless-lms/core/shared/ports';\nimport { noopLogger } from '@headless-lms/core/shared/logger';\n\nexport class EmailAdapter implements EmailSender {\n constructor(private readonly logger: Logger = noopLogger) {}\n\n async send(_message: EmailMessage): Promise<void> {\n this.logger.error('email send failed: no transport configured');\n throw new Error('not implemented');\n }\n}\n\nexport class StubTemplateRenderer implements TemplateRenderer {\n constructor(private readonly logger: Logger = noopLogger) {}\n\n async render(id: EmailTemplateId): Promise<never> {\n this.logger.error('template render failed: no renderer configured', { id });\n throw new Error('no template renderer configured');\n }\n}\n"],"mappings":";;AAIA,IAAa,eAAb,MAAiD;CAClB;CAA7B,YAAY,SAAkC,YAAY;EAA7B,KAAA,SAAA;CAA8B;CAE3D,MAAM,KAAK,UAAuC;EAChD,KAAK,OAAO,MAAM,4CAA4C;EAC9D,MAAM,IAAI,MAAM,iBAAiB;CACnC;AACF;AAEA,IAAa,uBAAb,MAA8D;CAC/B;CAA7B,YAAY,SAAkC,YAAY;EAA7B,KAAA,SAAA;CAA8B;CAE3D,MAAM,OAAO,IAAqC;EAChD,KAAK,OAAO,MAAM,kDAAkD,EAAE,GAAG,CAAC;EAC1E,MAAM,IAAI,MAAM,iCAAiC;CACnD;AACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { DomainEvent, EventBus, EventDefinition } from "@headless-lms/core/shared/ports";
|
|
2
|
+
//#region src/events/index.d.ts
|
|
3
|
+
declare class InMemoryEventBus implements EventBus {
|
|
4
|
+
private readonly handlers;
|
|
5
|
+
private readonly allHandlers;
|
|
6
|
+
publish(event: DomainEvent): Promise<void>;
|
|
7
|
+
subscribe<E extends DomainEvent>(definition: EventDefinition<E>, handler: (e: E) => Promise<void>): void;
|
|
8
|
+
subscribe(type: string, handler: (e: DomainEvent) => Promise<void>): void;
|
|
9
|
+
subscribeAll(handler: (e: DomainEvent) => Promise<void>): void;
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { InMemoryEventBus };
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/events/index.ts
|
|
2
|
+
var InMemoryEventBus = class {
|
|
3
|
+
handlers = /* @__PURE__ */ new Map();
|
|
4
|
+
allHandlers = [];
|
|
5
|
+
async publish(event) {
|
|
6
|
+
for (const handler of this.handlers.get(event.type) ?? []) await handler(event);
|
|
7
|
+
for (const handler of this.allHandlers) await handler(event);
|
|
8
|
+
}
|
|
9
|
+
subscribe(typeOrDefinition, handler) {
|
|
10
|
+
const type = typeof typeOrDefinition === "string" ? typeOrDefinition : typeOrDefinition.type;
|
|
11
|
+
const parsedHandler = typeof typeOrDefinition === "string" ? handler : async (event) => handler(typeOrDefinition.parse(event));
|
|
12
|
+
const list = this.handlers.get(type) ?? [];
|
|
13
|
+
list.push(parsedHandler);
|
|
14
|
+
this.handlers.set(type, list);
|
|
15
|
+
}
|
|
16
|
+
subscribeAll(handler) {
|
|
17
|
+
this.allHandlers.push(handler);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
export { InMemoryEventBus };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/events/index.ts"],"sourcesContent":["// In-process event bus. Implements the shared EventBus port: publish invokes\n// every handler subscribed to the event's type, sequentially, awaiting each,\n// then every all-events handler (subscribeAll), also sequentially awaited.\nimport type { DomainEvent, EventBus, EventDefinition } from '@headless-lms/core/shared/ports';\n\nexport class InMemoryEventBus implements EventBus {\n private readonly handlers = new Map<string, Array<(e: DomainEvent) => Promise<void>>>();\n private readonly allHandlers: Array<(e: DomainEvent) => Promise<void>> = [];\n\n async publish(event: DomainEvent): Promise<void> {\n for (const handler of this.handlers.get(event.type) ?? []) {\n await handler(event);\n }\n for (const handler of this.allHandlers) {\n await handler(event);\n }\n }\n\n subscribe<E extends DomainEvent>(\n definition: EventDefinition<E>,\n handler: (e: E) => Promise<void>,\n ): void;\n subscribe(type: string, handler: (e: DomainEvent) => Promise<void>): void;\n subscribe(\n typeOrDefinition: string | EventDefinition<DomainEvent>,\n handler: (e: DomainEvent) => Promise<void>,\n ): void {\n const type = typeof typeOrDefinition === 'string' ? typeOrDefinition : typeOrDefinition.type;\n const parsedHandler =\n typeof typeOrDefinition === 'string'\n ? handler\n : async (event: DomainEvent) => handler(typeOrDefinition.parse(event));\n const list = this.handlers.get(type) ?? [];\n list.push(parsedHandler);\n this.handlers.set(type, list);\n }\n\n subscribeAll(handler: (e: DomainEvent) => Promise<void>): void {\n this.allHandlers.push(handler);\n }\n}\n"],"mappings":";AAKA,IAAa,mBAAb,MAAkD;CAChD,2BAA4B,IAAI,IAAsD;CACtF,cAAyE,CAAC;CAE1E,MAAM,QAAQ,OAAmC;EAC/C,KAAK,MAAM,WAAW,KAAK,SAAS,IAAI,MAAM,IAAI,KAAK,CAAC,GACtD,MAAM,QAAQ,KAAK;EAErB,KAAK,MAAM,WAAW,KAAK,aACzB,MAAM,QAAQ,KAAK;CAEvB;CAOA,UACE,kBACA,SACM;EACN,MAAM,OAAO,OAAO,qBAAqB,WAAW,mBAAmB,iBAAiB;EACxF,MAAM,gBACJ,OAAO,qBAAqB,WACxB,UACA,OAAO,UAAuB,QAAQ,iBAAiB,MAAM,KAAK,CAAC;EACzE,MAAM,OAAO,KAAK,SAAS,IAAI,IAAI,KAAK,CAAC;EACzC,KAAK,KAAK,aAAa;EACvB,KAAK,SAAS,IAAI,MAAM,IAAI;CAC9B;CAEA,aAAa,SAAkD;EAC7D,KAAK,YAAY,KAAK,OAAO;CAC/B;AACF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { EventBus, Logger, OutboxRelay, OutboxStore } from "@headless-lms/core/shared/ports";
|
|
2
|
+
//#region src/events/outbox-relay.d.ts
|
|
3
|
+
/** Backoff base: first retry 5s after the first failure, doubling per attempt. */
|
|
4
|
+
declare const OUTBOX_BACKOFF_BASE_MS = 5000;
|
|
5
|
+
/** Backoff ceiling: retries never wait longer than 15 minutes. */
|
|
6
|
+
declare const OUTBOX_BACKOFF_MAX_MS: number;
|
|
7
|
+
/** Exponential backoff delay after a failure: min(base × 2^attempts, max),
|
|
8
|
+
* where `attempts` is the PRIOR failure count (0 on the first failure). */
|
|
9
|
+
declare function outboxBackoffMs(attempts: number): number;
|
|
10
|
+
interface PollingOutboxRelayConfig {
|
|
11
|
+
/** Master switch: false → start() is a no-op. */
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
/** Idle delay between polls. */
|
|
14
|
+
pollIntervalMs: number;
|
|
15
|
+
/** Max rows fetched/dispatched per tick; a full batch re-polls immediately. */
|
|
16
|
+
batchSize: number;
|
|
17
|
+
}
|
|
18
|
+
declare class PollingOutboxRelay implements OutboxRelay {
|
|
19
|
+
private readonly store;
|
|
20
|
+
private readonly bus;
|
|
21
|
+
private readonly config;
|
|
22
|
+
private readonly logger;
|
|
23
|
+
private pollTimer;
|
|
24
|
+
private running;
|
|
25
|
+
private inFlight;
|
|
26
|
+
constructor(store: OutboxStore, bus: EventBus, config: PollingOutboxRelayConfig, logger: Logger);
|
|
27
|
+
start(): void;
|
|
28
|
+
stop(): Promise<void>;
|
|
29
|
+
private schedule;
|
|
30
|
+
/** One poll: fetch a batch, dispatch in order, reschedule — immediately when
|
|
31
|
+
* the batch was full (drain bursts), else after the idle interval. Never
|
|
32
|
+
* throws: a poll failure is logged and retried next tick. */
|
|
33
|
+
private tick;
|
|
34
|
+
private dispatch;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { OUTBOX_BACKOFF_BASE_MS, OUTBOX_BACKOFF_MAX_MS, PollingOutboxRelay, PollingOutboxRelayConfig, outboxBackoffMs };
|
|
38
|
+
//# sourceMappingURL=outbox-relay.d.ts.map
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { OUTBOX_MAX_ATTEMPTS } from "@headless-lms/adapter-db";
|
|
2
|
+
//#region src/events/outbox-relay.ts
|
|
3
|
+
/** Backoff base: first retry 5s after the first failure, doubling per attempt. */
|
|
4
|
+
const OUTBOX_BACKOFF_BASE_MS = 5e3;
|
|
5
|
+
/** Backoff ceiling: retries never wait longer than 15 minutes. */
|
|
6
|
+
const OUTBOX_BACKOFF_MAX_MS = 15 * 6e4;
|
|
7
|
+
/** Exponential backoff delay after a failure: min(base × 2^attempts, max),
|
|
8
|
+
* where `attempts` is the PRIOR failure count (0 on the first failure). */
|
|
9
|
+
function outboxBackoffMs(attempts) {
|
|
10
|
+
return Math.min(OUTBOX_BACKOFF_BASE_MS * 2 ** attempts, OUTBOX_BACKOFF_MAX_MS);
|
|
11
|
+
}
|
|
12
|
+
var PollingOutboxRelay = class {
|
|
13
|
+
store;
|
|
14
|
+
bus;
|
|
15
|
+
config;
|
|
16
|
+
logger;
|
|
17
|
+
pollTimer;
|
|
18
|
+
running = false;
|
|
19
|
+
inFlight = Promise.resolve();
|
|
20
|
+
constructor(store, bus, config, logger) {
|
|
21
|
+
this.store = store;
|
|
22
|
+
this.bus = bus;
|
|
23
|
+
this.config = config;
|
|
24
|
+
this.logger = logger;
|
|
25
|
+
}
|
|
26
|
+
start() {
|
|
27
|
+
if (!this.config.enabled || this.running) return;
|
|
28
|
+
this.running = true;
|
|
29
|
+
this.schedule(this.config.pollIntervalMs);
|
|
30
|
+
this.logger.info("outbox relay started", {
|
|
31
|
+
pollIntervalMs: this.config.pollIntervalMs,
|
|
32
|
+
batchSize: this.config.batchSize
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async stop() {
|
|
36
|
+
if (this.pollTimer) clearTimeout(this.pollTimer);
|
|
37
|
+
this.pollTimer = void 0;
|
|
38
|
+
const wasRunning = this.running;
|
|
39
|
+
this.running = false;
|
|
40
|
+
await this.inFlight;
|
|
41
|
+
if (wasRunning) this.logger.info("outbox relay stopped");
|
|
42
|
+
}
|
|
43
|
+
schedule(delayMs) {
|
|
44
|
+
if (!this.running) return;
|
|
45
|
+
this.pollTimer = setTimeout(() => {
|
|
46
|
+
this.inFlight = this.tick();
|
|
47
|
+
}, delayMs);
|
|
48
|
+
}
|
|
49
|
+
/** One poll: fetch a batch, dispatch in order, reschedule — immediately when
|
|
50
|
+
* the batch was full (drain bursts), else after the idle interval. Never
|
|
51
|
+
* throws: a poll failure is logged and retried next tick. */
|
|
52
|
+
async tick() {
|
|
53
|
+
let fetched = 0;
|
|
54
|
+
try {
|
|
55
|
+
const batch = await this.store.fetchBatch(this.config.batchSize);
|
|
56
|
+
fetched = batch.length;
|
|
57
|
+
if (fetched > 0) this.logger.debug("outbox batch fetched", { count: fetched });
|
|
58
|
+
for (const message of batch) await this.dispatch(message);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
this.logger.error("outbox poll failed", { error: String(err) });
|
|
61
|
+
}
|
|
62
|
+
this.schedule(fetched >= this.config.batchSize ? 0 : this.config.pollIntervalMs);
|
|
63
|
+
}
|
|
64
|
+
async dispatch(message) {
|
|
65
|
+
try {
|
|
66
|
+
await this.bus.publish(message.payload);
|
|
67
|
+
await this.store.markProcessed(message.id);
|
|
68
|
+
this.logger.info("outbox event dispatched", {
|
|
69
|
+
id: message.id,
|
|
70
|
+
type: message.payload.type
|
|
71
|
+
});
|
|
72
|
+
} catch (err) {
|
|
73
|
+
const error = String(err);
|
|
74
|
+
const nextAttemptAt = new Date(Date.now() + outboxBackoffMs(message.attempts));
|
|
75
|
+
await this.store.markFailed(message.id, error, nextAttemptAt);
|
|
76
|
+
const attempt = message.attempts + 1;
|
|
77
|
+
const meta = {
|
|
78
|
+
id: message.id,
|
|
79
|
+
type: message.payload.type,
|
|
80
|
+
attempt,
|
|
81
|
+
nextAttemptAt: nextAttemptAt.toISOString(),
|
|
82
|
+
error
|
|
83
|
+
};
|
|
84
|
+
if (attempt >= OUTBOX_MAX_ATTEMPTS) this.logger.warn("outbox event parked", meta);
|
|
85
|
+
else this.logger.error("outbox dispatch failed", meta);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
//#endregion
|
|
90
|
+
export { OUTBOX_BACKOFF_BASE_MS, OUTBOX_BACKOFF_MAX_MS, PollingOutboxRelay, outboxBackoffMs };
|
|
91
|
+
|
|
92
|
+
//# sourceMappingURL=outbox-relay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outbox-relay.js","names":[],"sources":["../../src/events/outbox-relay.ts"],"sourcesContent":["// Same-process outbox relay (implements the core OutboxRelay port). A\n// setTimeout-CHAINED poller (never setInterval — a slow batch must not overlap\n// the next tick): each tick fetches unprocessed rows in id order and publishes\n// them to the EventBus.\n//\n// Delivery is at-least-once — handlers must be idempotent, keyed on event.id.\n// Ordering is skip-past-failures: a failing row is stamped with the error and\n// an exponential-backoff next_attempt_at (retried once due) while later rows\n// flow; the store stops fetching it after OUTBOX_MAX_ATTEMPTS failures.\nimport type {\n EventBus,\n Logger,\n OutboxMessage,\n OutboxRelay,\n OutboxStore,\n} from '@headless-lms/core/shared/ports';\nimport { OUTBOX_MAX_ATTEMPTS } from '@headless-lms/adapter-db';\n\n/** Backoff base: first retry 5s after the first failure, doubling per attempt. */\nexport const OUTBOX_BACKOFF_BASE_MS = 5_000;\n/** Backoff ceiling: retries never wait longer than 15 minutes. */\nexport const OUTBOX_BACKOFF_MAX_MS = 15 * 60_000;\n\n/** Exponential backoff delay after a failure: min(base × 2^attempts, max),\n * where `attempts` is the PRIOR failure count (0 on the first failure). */\nexport function outboxBackoffMs(attempts: number): number {\n return Math.min(OUTBOX_BACKOFF_BASE_MS * 2 ** attempts, OUTBOX_BACKOFF_MAX_MS);\n}\n\nexport interface PollingOutboxRelayConfig {\n /** Master switch: false → start() is a no-op. */\n enabled: boolean;\n /** Idle delay between polls. */\n pollIntervalMs: number;\n /** Max rows fetched/dispatched per tick; a full batch re-polls immediately. */\n batchSize: number;\n}\n\nexport class PollingOutboxRelay implements OutboxRelay {\n private pollTimer: NodeJS.Timeout | undefined;\n private running = false;\n private inFlight: Promise<void> = Promise.resolve();\n\n constructor(\n private readonly store: OutboxStore,\n private readonly bus: EventBus,\n private readonly config: PollingOutboxRelayConfig,\n private readonly logger: Logger,\n ) {}\n\n start(): void {\n if (!this.config.enabled || this.running) {\n return;\n }\n this.running = true;\n this.schedule(this.config.pollIntervalMs);\n this.logger.info('outbox relay started', {\n pollIntervalMs: this.config.pollIntervalMs,\n batchSize: this.config.batchSize,\n });\n }\n\n async stop(): Promise<void> {\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n }\n this.pollTimer = undefined;\n const wasRunning = this.running;\n this.running = false;\n await this.inFlight;\n if (wasRunning) {\n this.logger.info('outbox relay stopped');\n }\n }\n\n private schedule(delayMs: number): void {\n if (!this.running) {\n return;\n }\n this.pollTimer = setTimeout(() => {\n this.inFlight = this.tick();\n }, delayMs);\n }\n\n /** One poll: fetch a batch, dispatch in order, reschedule — immediately when\n * the batch was full (drain bursts), else after the idle interval. Never\n * throws: a poll failure is logged and retried next tick. */\n private async tick(): Promise<void> {\n let fetched = 0;\n try {\n const batch = await this.store.fetchBatch(this.config.batchSize);\n fetched = batch.length;\n if (fetched > 0) {\n this.logger.debug('outbox batch fetched', { count: fetched });\n }\n for (const message of batch) {\n await this.dispatch(message);\n }\n } catch (err) {\n this.logger.error('outbox poll failed', { error: String(err) });\n }\n this.schedule(fetched >= this.config.batchSize ? 0 : this.config.pollIntervalMs);\n }\n\n private async dispatch(message: OutboxMessage): Promise<void> {\n try {\n await this.bus.publish(message.payload);\n await this.store.markProcessed(message.id);\n this.logger.info('outbox event dispatched', {\n id: message.id,\n type: message.payload.type,\n });\n } catch (err) {\n const error = String(err);\n const nextAttemptAt = new Date(Date.now() + outboxBackoffMs(message.attempts));\n await this.store.markFailed(message.id, error, nextAttemptAt);\n const attempt = message.attempts + 1;\n const meta = {\n id: message.id,\n type: message.payload.type,\n attempt,\n nextAttemptAt: nextAttemptAt.toISOString(),\n error,\n };\n // The store stops fetching after OUTBOX_MAX_ATTEMPTS — this failure parks the row.\n if (attempt >= OUTBOX_MAX_ATTEMPTS) {\n this.logger.warn('outbox event parked', meta);\n } else {\n this.logger.error('outbox dispatch failed', meta);\n }\n }\n }\n}\n"],"mappings":";;;AAmBA,MAAa,yBAAyB;;AAEtC,MAAa,wBAAwB,KAAK;;;AAI1C,SAAgB,gBAAgB,UAA0B;CACxD,OAAO,KAAK,IAAI,yBAAyB,KAAK,UAAU,qBAAqB;AAC/E;AAWA,IAAa,qBAAb,MAAuD;CAMlC;CACA;CACA;CACA;CARnB;CACA,UAAkB;CAClB,WAAkC,QAAQ,QAAQ;CAElD,YACE,OACA,KACA,QACA,QACA;EAJiB,KAAA,QAAA;EACA,KAAA,MAAA;EACA,KAAA,SAAA;EACA,KAAA,SAAA;CAChB;CAEH,QAAc;EACZ,IAAI,CAAC,KAAK,OAAO,WAAW,KAAK,SAC/B;EAEF,KAAK,UAAU;EACf,KAAK,SAAS,KAAK,OAAO,cAAc;EACxC,KAAK,OAAO,KAAK,wBAAwB;GACvC,gBAAgB,KAAK,OAAO;GAC5B,WAAW,KAAK,OAAO;EACzB,CAAC;CACH;CAEA,MAAM,OAAsB;EAC1B,IAAI,KAAK,WACP,aAAa,KAAK,SAAS;EAE7B,KAAK,YAAY,KAAA;EACjB,MAAM,aAAa,KAAK;EACxB,KAAK,UAAU;EACf,MAAM,KAAK;EACX,IAAI,YACF,KAAK,OAAO,KAAK,sBAAsB;CAE3C;CAEA,SAAiB,SAAuB;EACtC,IAAI,CAAC,KAAK,SACR;EAEF,KAAK,YAAY,iBAAiB;GAChC,KAAK,WAAW,KAAK,KAAK;EAC5B,GAAG,OAAO;CACZ;;;;CAKA,MAAc,OAAsB;EAClC,IAAI,UAAU;EACd,IAAI;GACF,MAAM,QAAQ,MAAM,KAAK,MAAM,WAAW,KAAK,OAAO,SAAS;GAC/D,UAAU,MAAM;GAChB,IAAI,UAAU,GACZ,KAAK,OAAO,MAAM,wBAAwB,EAAE,OAAO,QAAQ,CAAC;GAE9D,KAAK,MAAM,WAAW,OACpB,MAAM,KAAK,SAAS,OAAO;EAE/B,SAAS,KAAK;GACZ,KAAK,OAAO,MAAM,sBAAsB,EAAE,OAAO,OAAO,GAAG,EAAE,CAAC;EAChE;EACA,KAAK,SAAS,WAAW,KAAK,OAAO,YAAY,IAAI,KAAK,OAAO,cAAc;CACjF;CAEA,MAAc,SAAS,SAAuC;EAC5D,IAAI;GACF,MAAM,KAAK,IAAI,QAAQ,QAAQ,OAAO;GACtC,MAAM,KAAK,MAAM,cAAc,QAAQ,EAAE;GACzC,KAAK,OAAO,KAAK,2BAA2B;IAC1C,IAAI,QAAQ;IACZ,MAAM,QAAQ,QAAQ;GACxB,CAAC;EACH,SAAS,KAAK;GACZ,MAAM,QAAQ,OAAO,GAAG;GACxB,MAAM,gBAAgB,IAAI,KAAK,KAAK,IAAI,IAAI,gBAAgB,QAAQ,QAAQ,CAAC;GAC7E,MAAM,KAAK,MAAM,WAAW,QAAQ,IAAI,OAAO,aAAa;GAC5D,MAAM,UAAU,QAAQ,WAAW;GACnC,MAAM,OAAO;IACX,IAAI,QAAQ;IACZ,MAAM,QAAQ,QAAQ;IACtB;IACA,eAAe,cAAc,YAAY;IACzC;GACF;GAEA,IAAI,WAAW,qBACb,KAAK,OAAO,KAAK,uBAAuB,IAAI;QAE5C,KAAK,OAAO,MAAM,0BAA0B,IAAI;EAEpD;CACF;AACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { RequestLogContext, requestLogContext } from "./request-context.js";
|
|
2
|
+
import { DestinationStream, Logger as PinoInstance } from "pino";
|
|
3
|
+
import { Logger } from "@headless-lms/core/shared/ports";
|
|
4
|
+
//#region src/logging/index.d.ts
|
|
5
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
6
|
+
declare class PinoLogger implements Logger {
|
|
7
|
+
private readonly instance;
|
|
8
|
+
constructor(instance: PinoInstance);
|
|
9
|
+
debug(msg: string, meta?: Record<string, unknown>): void;
|
|
10
|
+
info(msg: string, meta?: Record<string, unknown>): void;
|
|
11
|
+
warn(msg: string, meta?: Record<string, unknown>): void;
|
|
12
|
+
error(msg: string, meta?: Record<string, unknown>): void;
|
|
13
|
+
child(bindings: Record<string, unknown>): Logger;
|
|
14
|
+
}
|
|
15
|
+
/** The process-wide root: JSON lines, Error serialization under `err`.
|
|
16
|
+
* `destination` exists for tests; omitted → stdout. */
|
|
17
|
+
declare function createRootLogger(level: LogLevel, destination?: DestinationStream): {
|
|
18
|
+
instance: PinoInstance;
|
|
19
|
+
logger: Logger;
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
export { LogLevel, type PinoInstance, PinoLogger, type RequestLogContext, createRootLogger, requestLogContext };
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { requestLogContext } from "./request-context.js";
|
|
2
|
+
import { pino, stdSerializers } from "pino";
|
|
3
|
+
//#region src/logging/index.ts
|
|
4
|
+
var PinoLogger = class PinoLogger {
|
|
5
|
+
instance;
|
|
6
|
+
constructor(instance) {
|
|
7
|
+
this.instance = instance;
|
|
8
|
+
}
|
|
9
|
+
debug(msg, meta) {
|
|
10
|
+
this.instance.debug(meta ?? {}, msg);
|
|
11
|
+
}
|
|
12
|
+
info(msg, meta) {
|
|
13
|
+
this.instance.info(meta ?? {}, msg);
|
|
14
|
+
}
|
|
15
|
+
warn(msg, meta) {
|
|
16
|
+
this.instance.warn(meta ?? {}, msg);
|
|
17
|
+
}
|
|
18
|
+
error(msg, meta) {
|
|
19
|
+
this.instance.error(meta ?? {}, msg);
|
|
20
|
+
}
|
|
21
|
+
child(bindings) {
|
|
22
|
+
return new PinoLogger(this.instance.child(bindings));
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
/** The process-wide root: JSON lines, Error serialization under `err`.
|
|
26
|
+
* `destination` exists for tests; omitted → stdout. */
|
|
27
|
+
function createRootLogger(level, destination) {
|
|
28
|
+
const instance = pino({
|
|
29
|
+
level,
|
|
30
|
+
serializers: { err: stdSerializers.err },
|
|
31
|
+
mixin: () => requestLogContext.current()
|
|
32
|
+
}, destination);
|
|
33
|
+
return {
|
|
34
|
+
instance,
|
|
35
|
+
logger: new PinoLogger(instance)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { PinoLogger, createRootLogger, requestLogContext };
|
|
40
|
+
|
|
41
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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,EAAE,KAAK,eAAe,IAAI;EAEvC,aAAa,kBAAkB,QAAQ;CACzC,GACA,WACF;CACA,OAAO;EAAE;EAAU,QAAQ,IAAI,WAAW,QAAQ;CAAE;AACtD"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/logging/request-context.d.ts
|
|
2
|
+
interface RequestLogContext {
|
|
3
|
+
/** Runs `fn` with `fields` as the ambient log context for its async scope. */
|
|
4
|
+
run<T>(fields: Record<string, unknown>, fn: () => T): T;
|
|
5
|
+
/** Merges fields into the current request's context; noop outside a request. */
|
|
6
|
+
set(fields: Record<string, unknown>): void;
|
|
7
|
+
/** A copy of the current context ({} outside a request) — safe for pino's mutating mixin merge. */
|
|
8
|
+
current(): Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
declare const requestLogContext: RequestLogContext;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { RequestLogContext, requestLogContext };
|
|
13
|
+
//# sourceMappingURL=request-context.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
//#region src/logging/request-context.ts
|
|
3
|
+
const storage = new AsyncLocalStorage();
|
|
4
|
+
const requestLogContext = {
|
|
5
|
+
run(fields, fn) {
|
|
6
|
+
return storage.run(fields, fn);
|
|
7
|
+
},
|
|
8
|
+
set(fields) {
|
|
9
|
+
const store = storage.getStore();
|
|
10
|
+
if (store) Object.assign(store, fields);
|
|
11
|
+
},
|
|
12
|
+
current() {
|
|
13
|
+
return { ...storage.getStore() };
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
export { requestLogContext };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=request-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-context.js","names":[],"sources":["../../src/logging/request-context.ts"],"sourcesContent":["// Request-scoped log context. The HTTP layer enters a store per request and the\n// root pino mixin folds it into every line logged inside that request — so\n// lines emitted deep in adapters (e.g. a failed invite email inside better-auth\n// callbacks) still carry the reqId/orgId of the request that triggered them.\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\nconst storage = new AsyncLocalStorage<Record<string, unknown>>();\n\nexport interface RequestLogContext {\n /** Runs `fn` with `fields` as the ambient log context for its async scope. */\n run<T>(fields: Record<string, unknown>, fn: () => T): T;\n /** Merges fields into the current request's context; noop outside a request. */\n set(fields: Record<string, unknown>): void;\n /** A copy of the current context ({} outside a request) — safe for pino's mutating mixin merge. */\n current(): Record<string, unknown>;\n}\n\nexport const requestLogContext: RequestLogContext = {\n run(fields, fn) {\n return storage.run(fields, fn);\n },\n set(fields) {\n const store = storage.getStore();\n if (store) {\n Object.assign(store, fields);\n }\n },\n current() {\n return { ...storage.getStore() };\n },\n};\n"],"mappings":";;AAMA,MAAM,UAAU,IAAI,kBAA2C;AAW/D,MAAa,oBAAuC;CAClD,IAAI,QAAQ,IAAI;EACd,OAAO,QAAQ,IAAI,QAAQ,EAAE;CAC/B;CACA,IAAI,QAAQ;EACV,MAAM,QAAQ,QAAQ,SAAS;EAC/B,IAAI,OACF,OAAO,OAAO,OAAO,MAAM;CAE/B;CACA,UAAU;EACR,OAAO,EAAE,GAAG,QAAQ,SAAS,EAAE;CACjC;AACF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Logger, ObjectStorage, PresignDownloadInput, PresignedUpload, StoredObjectInfo } from "@headless-lms/core/shared/ports";
|
|
2
|
+
//#region src/storage/index.d.ts
|
|
3
|
+
declare class StorageAdapter implements ObjectStorage {
|
|
4
|
+
private readonly logger;
|
|
5
|
+
constructor(logger?: Logger);
|
|
6
|
+
presignUpload(_input: {
|
|
7
|
+
key: string;
|
|
8
|
+
contentType?: string;
|
|
9
|
+
expiresInSeconds?: number;
|
|
10
|
+
}): Promise<PresignedUpload>;
|
|
11
|
+
presignDownload(_input: PresignDownloadInput): Promise<string>;
|
|
12
|
+
stat(_key: string): Promise<StoredObjectInfo | null>;
|
|
13
|
+
remove(_key: string): Promise<void>;
|
|
14
|
+
private fail;
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { StorageAdapter };
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { noopLogger } from "@headless-lms/core/shared/logger";
|
|
2
|
+
//#region src/storage/index.ts
|
|
3
|
+
var StorageAdapter = class {
|
|
4
|
+
logger;
|
|
5
|
+
constructor(logger = noopLogger) {
|
|
6
|
+
this.logger = logger;
|
|
7
|
+
}
|
|
8
|
+
async presignUpload(_input) {
|
|
9
|
+
return this.fail("presignUpload");
|
|
10
|
+
}
|
|
11
|
+
async presignDownload(_input) {
|
|
12
|
+
return this.fail("presignDownload");
|
|
13
|
+
}
|
|
14
|
+
async stat(_key) {
|
|
15
|
+
return this.fail("stat");
|
|
16
|
+
}
|
|
17
|
+
async remove(_key) {
|
|
18
|
+
return this.fail("remove");
|
|
19
|
+
}
|
|
20
|
+
fail(op) {
|
|
21
|
+
this.logger.error(`storage ${op} failed: no storage adapter configured`);
|
|
22
|
+
throw new Error("not implemented");
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
//#endregion
|
|
26
|
+
export { StorageAdapter };
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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;CACtB;CAA7B,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,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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { AutomationDispatch, AutomationEngine, AutomationExecutor } from "@headless-lms/core/types";
|
|
2
|
+
//#region src/workflows/index.d.ts
|
|
3
|
+
declare class InlineAutomationEngine implements AutomationEngine {
|
|
4
|
+
private executor;
|
|
5
|
+
register(executor: AutomationExecutor): void;
|
|
6
|
+
dispatch(d: AutomationDispatch): Promise<void>;
|
|
7
|
+
start(): Promise<void>;
|
|
8
|
+
stop(): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
export { InlineAutomationEngine };
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//#region src/workflows/index.ts
|
|
2
|
+
var InlineAutomationEngine = class {
|
|
3
|
+
executor;
|
|
4
|
+
register(executor) {
|
|
5
|
+
this.executor = executor;
|
|
6
|
+
}
|
|
7
|
+
async dispatch(d) {
|
|
8
|
+
if (!this.executor) throw new Error("InlineAutomationEngine.dispatch called before register()");
|
|
9
|
+
const executor = this.executor;
|
|
10
|
+
const results = [];
|
|
11
|
+
for (let index = 0; index < d.actions.length; index++) try {
|
|
12
|
+
results.push(await executor.runAction(d, index));
|
|
13
|
+
} catch (err) {
|
|
14
|
+
const message = err?.message ?? err;
|
|
15
|
+
results.push({
|
|
16
|
+
index,
|
|
17
|
+
type: d.actions[index].type,
|
|
18
|
+
status: "failed",
|
|
19
|
+
error: String(message)
|
|
20
|
+
});
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
await executor.finalize(d, results);
|
|
24
|
+
}
|
|
25
|
+
async start() {}
|
|
26
|
+
async stop() {}
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export { InlineAutomationEngine };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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 const message = (err as { message?: unknown } | undefined)?.message ?? err;\n results.push({ index, type: d.actions[index]!.type, status: 'failed', error: String(message) });\n break;\n }\n }\n\n await executor.finalize(d, results);\n }\n\n async start(): Promise<void> {}\n\n async stop(): Promise<void> {}\n}\n"],"mappings":";AASA,IAAa,yBAAb,MAAgE;CAC9D;CAEA,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,MAAM,UAAW,KAA2C,WAAW;GACvE,QAAQ,KAAK;IAAE;IAAO,MAAM,EAAE,QAAQ,MAAM,CAAE;IAAM,QAAQ;IAAU,OAAO,OAAO,OAAO;GAAE,CAAC;GAC9F;EACF;EAGF,MAAM,SAAS,SAAS,GAAG,OAAO;CACpC;CAEA,MAAM,QAAuB,CAAC;CAE9B,MAAM,OAAsB,CAAC;AAC/B"}
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@headless-lms/adapter-defaults",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
"./email": {
|
|
7
|
+
"types": "./dist/email/index.d.ts",
|
|
8
|
+
"default": "./dist/email/index.js"
|
|
9
|
+
},
|
|
10
|
+
"./storage": {
|
|
11
|
+
"types": "./dist/storage/index.d.ts",
|
|
12
|
+
"default": "./dist/storage/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./events": {
|
|
15
|
+
"types": "./dist/events/index.d.ts",
|
|
16
|
+
"default": "./dist/events/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./events/outbox-relay": {
|
|
19
|
+
"types": "./dist/events/outbox-relay.d.ts",
|
|
20
|
+
"default": "./dist/events/outbox-relay.js"
|
|
21
|
+
},
|
|
22
|
+
"./logging": {
|
|
23
|
+
"types": "./dist/logging/index.d.ts",
|
|
24
|
+
"default": "./dist/logging/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./logging/request-context": {
|
|
27
|
+
"types": "./dist/logging/request-context.d.ts",
|
|
28
|
+
"default": "./dist/logging/request-context.js"
|
|
29
|
+
},
|
|
30
|
+
"./workflows": {
|
|
31
|
+
"types": "./dist/workflows/index.d.ts",
|
|
32
|
+
"default": "./dist/workflows/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist"
|
|
37
|
+
],
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"pino": "^10.3.1",
|
|
40
|
+
"@headless-lms/adapter-db": "0.1.0",
|
|
41
|
+
"@headless-lms/core": "0.1.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^22.10.2",
|
|
45
|
+
"tsdown": "^0.22.9",
|
|
46
|
+
"typescript": "^5.7.2",
|
|
47
|
+
"vitest": "^2.1.8"
|
|
48
|
+
},
|
|
49
|
+
"description": "Default in-process adapters (email, storage, events, logging, workflows) for the headless LMS",
|
|
50
|
+
"license": "MIT",
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "git+https://github.com/mdwt/headless-lms.git",
|
|
54
|
+
"directory": "adapters/defaults"
|
|
55
|
+
},
|
|
56
|
+
"homepage": "https://github.com/mdwt/headless-lms#readme",
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/mdwt/headless-lms/issues"
|
|
59
|
+
},
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"build": "tsdown",
|
|
65
|
+
"test": "vitest run",
|
|
66
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
67
|
+
}
|
|
68
|
+
}
|