@porulle/core 0.35.0 → 0.35.1
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/kernel/hooks/registry.d.ts +4 -0
- package/dist/kernel/hooks/registry.d.ts.map +1 -1
- package/dist/kernel/hooks/registry.js +22 -0
- package/dist/kernel/plugin/manifest.d.ts +9 -0
- package/dist/kernel/plugin/manifest.d.ts.map +1 -1
- package/dist/kernel/plugin/manifest.js +5 -0
- package/dist/runtime/kernel.d.ts.map +1 -1
- package/dist/runtime/kernel.js +10 -2
- package/package.json +1 -1
- package/src/kernel/hooks/registry.ts +24 -0
- package/src/kernel/plugin/manifest.ts +13 -0
- package/src/runtime/kernel.ts +9 -2
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export type HookHandler = (...args: never[]) => unknown;
|
|
2
|
+
/** Declare that this handler must run inside the writing transaction. Called by the manifest merge. */
|
|
3
|
+
export declare function markHookInTransaction(handler: unknown): void;
|
|
4
|
+
/** Whether a plugin asked for this handler to run in-transaction. Read by the kernel at boot. */
|
|
5
|
+
export declare function isHookMarkedInTransaction(handler: unknown): boolean;
|
|
2
6
|
export declare class HookRegistry {
|
|
3
7
|
private registry;
|
|
4
8
|
private inTransactionHandlers;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/kernel/hooks/registry.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/kernel/hooks/registry.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC;AAgBxD,uGAAuG;AACvG,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE5D;AAED,iGAAiG;AACjG,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAEnE;AAQD,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAgC;IAChD,OAAO,CAAC,qBAAqB,CAA8B;IAC3D,OAAO,CAAC,MAAM,CAAC,CAAiE;IAEhF,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,IAAI;IAKpE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI;IAKpD,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI;IAKjE,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI;IAKrD,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI;IAKlE,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO;IAIhD,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,EAAE;IAMxC;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,GAAG,IAAI;IAIjF,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAcxD,OAAO,CAAC,WAAW;CASpB"}
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handlers a PLUGIN asked to run inside the writing transaction.
|
|
3
|
+
*
|
|
4
|
+
* Module-level rather than per-registry because a plugin's declaration and its registration are
|
|
5
|
+
* separated by the manifest merge: `manifest.hooks()` returns `{ key, handler, inTransaction }`
|
|
6
|
+
* records, which are flattened into `config.hooks` as a bare `key -> handler[]` map long before any
|
|
7
|
+
* HookRegistry exists. Marking the function itself is the only channel that survives that.
|
|
8
|
+
*
|
|
9
|
+
* Shipped in 0.35.1 because 0.35.0 made after-commit the default with no way for a plugin to opt
|
|
10
|
+
* out, which silently moved `loom_projection_pending` — a transactional outbox whose whole point is
|
|
11
|
+
* committing with the write it records — to after the commit.
|
|
12
|
+
*/
|
|
13
|
+
const pluginInTransactionHandlers = new WeakSet();
|
|
14
|
+
/** Declare that this handler must run inside the writing transaction. Called by the manifest merge. */
|
|
15
|
+
export function markHookInTransaction(handler) {
|
|
16
|
+
if (typeof handler === "function")
|
|
17
|
+
pluginInTransactionHandlers.add(handler);
|
|
18
|
+
}
|
|
19
|
+
/** Whether a plugin asked for this handler to run in-transaction. Read by the kernel at boot. */
|
|
20
|
+
export function isHookMarkedInTransaction(handler) {
|
|
21
|
+
return typeof handler === "function" && pluginInTransactionHandlers.has(handler);
|
|
22
|
+
}
|
|
1
23
|
export class HookRegistry {
|
|
2
24
|
registry = new Map();
|
|
3
25
|
inTransactionHandlers = new WeakSet();
|
|
@@ -28,6 +28,15 @@ export type PluginRouteRegistration = {
|
|
|
28
28
|
export interface PluginHookRegistration {
|
|
29
29
|
key: string;
|
|
30
30
|
handler: (...args: unknown[]) => unknown;
|
|
31
|
+
/**
|
|
32
|
+
* Run this hook INSIDE the writing transaction instead of after it commits.
|
|
33
|
+
*
|
|
34
|
+
* The default is after-commit and is correct for anything with an external effect — a webhook, a
|
|
35
|
+
* search-index write, an email — none of which may announce a write that can still roll back.
|
|
36
|
+
* Set this only for an OUTBOX WRITER: a hook whose own write must commit or roll back together
|
|
37
|
+
* with the row it records. `loom_projection_pending` is the case this exists for.
|
|
38
|
+
*/
|
|
39
|
+
inTransaction?: boolean;
|
|
31
40
|
}
|
|
32
41
|
export interface PluginContext {
|
|
33
42
|
config: CommerceConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/kernel/plugin/manifest.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/kernel/plugin/manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { markHookInTransaction } from "../hooks/registry.js";
|
|
1
2
|
import { resolveOrgIdForCommerce } from "../../auth/org.js";
|
|
2
3
|
import { isRoutePermissionGuard, markRoutePermissionGuard } from "../../interfaces/rest/utils.js";
|
|
3
4
|
import { createScopedDb } from "../database/scoped-db.js";
|
|
@@ -141,6 +142,10 @@ export function defineCommercePlugin(manifest) {
|
|
|
141
142
|
...(result.hooks ?? {}),
|
|
142
143
|
};
|
|
143
144
|
for (const reg of registrations) {
|
|
145
|
+
// The marker rides on the function because this map is a bare key -> handler[] and cannot
|
|
146
|
+
// carry it; the kernel reads it back when it registers these at boot.
|
|
147
|
+
if (reg.inTransaction)
|
|
148
|
+
markHookInTransaction(reg.handler);
|
|
144
149
|
hookMap[reg.key] = [...(hookMap[reg.key] ?? []), reg.handler];
|
|
145
150
|
}
|
|
146
151
|
result = { ...result, hooks: hookMap };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kernel.d.ts","sourceRoot":"","sources":["../../src/runtime/kernel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAqBzD,OAAO,EAGL,KAAK,MAAM,EACX,KAAK,sBAAsB,EAC5B,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;AAC/C,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EACL,kBAAkB,EAClB,8BAA8B,GAC/B,MAAM,qBAAqB,CAAC;AAE7B,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"kernel.d.ts","sourceRoot":"","sources":["../../src/runtime/kernel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAqBzD,OAAO,EAGL,KAAK,MAAM,EACX,KAAK,sBAAsB,EAC5B,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;AAC/C,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EACL,kBAAkB,EAClB,8BAA8B,GAC/B,MAAM,qBAAqB,CAAC;AAE7B,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAiJ3D"}
|
package/dist/runtime/kernel.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HookRegistry } from "../kernel/hooks/registry.js";
|
|
1
|
+
import { HookRegistry, isHookMarkedInTransaction } from "../kernel/hooks/registry.js";
|
|
2
2
|
import { createDatabaseConnection } from "../kernel/database/adapter.js";
|
|
3
3
|
import { topoSortModules } from "../kernel/module/index.js";
|
|
4
4
|
import { WebhookDeliveryWorker } from "../modules/webhooks/worker.js";
|
|
@@ -115,7 +115,15 @@ export function createKernel(config) {
|
|
|
115
115
|
};
|
|
116
116
|
for (const [key, handlers] of Object.entries(config.hooks ?? {})) {
|
|
117
117
|
for (const handler of handlers) {
|
|
118
|
-
|
|
118
|
+
// A plugin that declared `inTransaction` gets appendInTransaction, so its hook runs inside
|
|
119
|
+
// the writing transaction rather than in the after-commit drain. Everything else defaults to
|
|
120
|
+
// after-commit, which is what 0.35.0 established.
|
|
121
|
+
if (isHookMarkedInTransaction(handler)) {
|
|
122
|
+
hooks.appendInTransaction(key, handler);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
hooks.append(key, handler);
|
|
126
|
+
}
|
|
119
127
|
}
|
|
120
128
|
}
|
|
121
129
|
for (const model of config.analytics?.models ?? []) {
|
package/package.json
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
export type HookHandler = (...args: never[]) => unknown;
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Handlers a PLUGIN asked to run inside the writing transaction.
|
|
5
|
+
*
|
|
6
|
+
* Module-level rather than per-registry because a plugin's declaration and its registration are
|
|
7
|
+
* separated by the manifest merge: `manifest.hooks()` returns `{ key, handler, inTransaction }`
|
|
8
|
+
* records, which are flattened into `config.hooks` as a bare `key -> handler[]` map long before any
|
|
9
|
+
* HookRegistry exists. Marking the function itself is the only channel that survives that.
|
|
10
|
+
*
|
|
11
|
+
* Shipped in 0.35.1 because 0.35.0 made after-commit the default with no way for a plugin to opt
|
|
12
|
+
* out, which silently moved `loom_projection_pending` — a transactional outbox whose whole point is
|
|
13
|
+
* committing with the write it records — to after the commit.
|
|
14
|
+
*/
|
|
15
|
+
const pluginInTransactionHandlers = new WeakSet<object>();
|
|
16
|
+
|
|
17
|
+
/** Declare that this handler must run inside the writing transaction. Called by the manifest merge. */
|
|
18
|
+
export function markHookInTransaction(handler: unknown): void {
|
|
19
|
+
if (typeof handler === "function") pluginInTransactionHandlers.add(handler as object);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Whether a plugin asked for this handler to run in-transaction. Read by the kernel at boot. */
|
|
23
|
+
export function isHookMarkedInTransaction(handler: unknown): boolean {
|
|
24
|
+
return typeof handler === "function" && pluginInTransactionHandlers.has(handler as object);
|
|
25
|
+
}
|
|
26
|
+
|
|
3
27
|
type HookEntry = {
|
|
4
28
|
prepended: HookHandler[];
|
|
5
29
|
configured: HookHandler[];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Hono } from "hono";
|
|
2
|
+
import { markHookInTransaction } from "../hooks/registry.js";
|
|
2
3
|
import type { OpenAPIHono, RouteConfig } from "@hono/zod-openapi";
|
|
3
4
|
import type { PluginDb } from "../database/plugin-types.js";
|
|
4
5
|
import type { DatabaseAdapter } from "../database/adapter.js";
|
|
@@ -43,6 +44,15 @@ export type PluginRouteRegistration =
|
|
|
43
44
|
export interface PluginHookRegistration {
|
|
44
45
|
key: string;
|
|
45
46
|
handler: (...args: unknown[]) => unknown;
|
|
47
|
+
/**
|
|
48
|
+
* Run this hook INSIDE the writing transaction instead of after it commits.
|
|
49
|
+
*
|
|
50
|
+
* The default is after-commit and is correct for anything with an external effect — a webhook, a
|
|
51
|
+
* search-index write, an email — none of which may announce a write that can still roll back.
|
|
52
|
+
* Set this only for an OUTBOX WRITER: a hook whose own write must commit or roll back together
|
|
53
|
+
* with the row it records. `loom_projection_pending` is the case this exists for.
|
|
54
|
+
*/
|
|
55
|
+
inTransaction?: boolean;
|
|
46
56
|
}
|
|
47
57
|
|
|
48
58
|
// ─── Plugin Context (available to routes at boot time) ───────
|
|
@@ -294,6 +304,9 @@ export function defineCommercePlugin(
|
|
|
294
304
|
...(result.hooks ?? {}),
|
|
295
305
|
};
|
|
296
306
|
for (const reg of registrations) {
|
|
307
|
+
// The marker rides on the function because this map is a bare key -> handler[] and cannot
|
|
308
|
+
// carry it; the kernel reads it back when it registers these at boot.
|
|
309
|
+
if (reg.inTransaction) markHookInTransaction(reg.handler);
|
|
297
310
|
hookMap[reg.key] = [...(hookMap[reg.key] ?? []), reg.handler];
|
|
298
311
|
}
|
|
299
312
|
result = { ...result, hooks: hookMap };
|
package/src/runtime/kernel.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CommerceConfig } from "../config/types.js";
|
|
2
|
-
import { HookRegistry, type HookHandler } from "../kernel/hooks/registry.js";
|
|
2
|
+
import { HookRegistry, isHookMarkedInTransaction, type HookHandler } from "../kernel/hooks/registry.js";
|
|
3
3
|
import { createDatabaseConnection } from "../kernel/database/adapter.js";
|
|
4
4
|
import type { DrizzleDatabase } from "../kernel/database/drizzle-db.js";
|
|
5
5
|
import type { AppModule } from "../kernel/module/index.js";
|
|
@@ -162,7 +162,14 @@ export function createKernel(config: CommerceConfig): Kernel {
|
|
|
162
162
|
|
|
163
163
|
for (const [key, handlers] of Object.entries(config.hooks ?? {})) {
|
|
164
164
|
for (const handler of handlers) {
|
|
165
|
-
|
|
165
|
+
// A plugin that declared `inTransaction` gets appendInTransaction, so its hook runs inside
|
|
166
|
+
// the writing transaction rather than in the after-commit drain. Everything else defaults to
|
|
167
|
+
// after-commit, which is what 0.35.0 established.
|
|
168
|
+
if (isHookMarkedInTransaction(handler)) {
|
|
169
|
+
hooks.appendInTransaction(key, handler as HookHandler);
|
|
170
|
+
} else {
|
|
171
|
+
hooks.append(key, handler as HookHandler);
|
|
172
|
+
}
|
|
166
173
|
}
|
|
167
174
|
}
|
|
168
175
|
|