@teamscala/service-boot 0.23.526 → 0.23.528
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/package.json +2 -4
- package/src/agreement-gate/signing-session.ts +21 -36
- package/src/agreement-gate.ts +2 -2
- package/src/expand-roster.ts +17 -27
- package/src/pg-boss-mount.ts +8 -7
- package/src/register-registry-page-route/warn-uncovered-blocks.ts +3 -20
- package/src/two-phase-full-app.ts +1 -0
- package/src/pg-boss-handler-registry.ts +0 -15
package/package.json
CHANGED
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"@teamscala/event-log": "*",
|
|
18
18
|
"@teamscala/file-lock": "*",
|
|
19
19
|
"@teamscala/formatters": "*",
|
|
20
|
-
"@teamscala/generic-registry": "*",
|
|
21
20
|
"@teamscala/hono-boundary": "*",
|
|
22
21
|
"@teamscala/http": "*",
|
|
23
22
|
"@teamscala/logger": "*",
|
|
@@ -27,6 +26,7 @@
|
|
|
27
26
|
"@teamscala/os": "*",
|
|
28
27
|
"@teamscala/publish-registry": "*",
|
|
29
28
|
"@teamscala/puck-boundary": "*",
|
|
29
|
+
"@teamscala/puck-tree": "*",
|
|
30
30
|
"@teamscala/resolver-engine": "*",
|
|
31
31
|
"@teamscala/timing": "*",
|
|
32
32
|
"@teamscala/tool-executor": "*",
|
|
@@ -160,8 +160,6 @@
|
|
|
160
160
|
"./page-server-loader-graph.ts": "./src/page-server-loader-graph.ts",
|
|
161
161
|
"./page-server-orpc/server-client": "./src/page-server-orpc/server-client.ts",
|
|
162
162
|
"./page-server-orpc/server-client.ts": "./src/page-server-orpc/server-client.ts",
|
|
163
|
-
"./pg-boss-handler-registry": "./src/pg-boss-handler-registry.ts",
|
|
164
|
-
"./pg-boss-handler-registry.ts": "./src/pg-boss-handler-registry.ts",
|
|
165
163
|
"./pg-boss-mount": "./src/pg-boss-mount.ts",
|
|
166
164
|
"./pg-boss-mount.ts": "./src/pg-boss-mount.ts",
|
|
167
165
|
"./proposal-authorized-emails": "./src/proposal-authorized-emails.ts",
|
|
@@ -292,5 +290,5 @@
|
|
|
292
290
|
"repository": "https://github.com/teamgbg/service-boot",
|
|
293
291
|
"sideEffects": false,
|
|
294
292
|
"type": "module",
|
|
295
|
-
"version": "0.23.
|
|
293
|
+
"version": "0.23.528"
|
|
296
294
|
}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
buildCertificateData,
|
|
14
14
|
type CertificateData,
|
|
15
15
|
} from "@teamscala/agreement-generation/agreement-certificate";
|
|
16
|
+
import { collectBlocks, forEachContent, type PuckData } from "@teamscala/puck-tree/collect-blocks";
|
|
16
17
|
import { findFirstAgreement } from "./agreement-lookup.ts";
|
|
17
18
|
import { resolveAgreementViewerDirectory } from "./resolution-caches.ts";
|
|
18
19
|
|
|
@@ -38,24 +39,15 @@ export interface SigningSession {
|
|
|
38
39
|
certificate: CertificateData | null;
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
export function* regionContents(
|
|
42
|
+
export function* regionTrees(
|
|
43
43
|
resolved: Record<string, unknown>,
|
|
44
|
-
): Generator<{
|
|
44
|
+
): Generator<{ content?: unknown[] } & PuckData> {
|
|
45
45
|
for (const value of Object.values(resolved)) {
|
|
46
46
|
const pd = (value as { puck_data?: { content?: unknown[] } } | null)?.puck_data;
|
|
47
|
-
|
|
48
|
-
if (pd && Array.isArray(content)) {
|
|
49
|
-
yield { pd, content: content as ContentBlock[] };
|
|
50
|
-
}
|
|
47
|
+
if (pd && Array.isArray(pd.content)) yield pd as { content?: unknown[] } & PuckData;
|
|
51
48
|
}
|
|
52
49
|
}
|
|
53
50
|
|
|
54
|
-
/**
|
|
55
|
-
* Locate the agreement gate block(s) across regions. A gate is the SignIn block
|
|
56
|
-
* in `gate` mode whose field is NOT "proposalId" (that field marks a proposal
|
|
57
|
-
* gate); the agreement id lives in `gateContextValue`.
|
|
58
|
-
*/
|
|
59
51
|
export function findAgreementGate(resolved: Record<string, unknown>): {
|
|
60
52
|
agreementUuid: string;
|
|
61
53
|
gateRegions: { pd: { content?: unknown[] }; content: ContentBlock[]; gateIdx: number }[];
|
|
@@ -66,17 +58,19 @@ export function findAgreementGate(resolved: Record<string, unknown>): {
|
|
|
66
58
|
content: ContentBlock[];
|
|
67
59
|
gateIdx: number;
|
|
68
60
|
}[] = [];
|
|
69
|
-
for (const
|
|
70
|
-
|
|
71
|
-
(
|
|
72
|
-
b
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
61
|
+
for (const pd of regionTrees(resolved)) {
|
|
62
|
+
forEachContent(pd, (content) => {
|
|
63
|
+
const gateIdx = (content as ContentBlock[]).findIndex(
|
|
64
|
+
(b) =>
|
|
65
|
+
b?.type === "SignIn" &&
|
|
66
|
+
b?.props?.mode === "gate" &&
|
|
67
|
+
b?.props?.gateContextField !== "proposalId",
|
|
68
|
+
);
|
|
69
|
+
if (gateIdx < 0) return;
|
|
70
|
+
const uuid = (content as ContentBlock[])[gateIdx].props?.gateContextValue;
|
|
71
|
+
if (typeof uuid === "string" && uuid) agreementUuid = uuid;
|
|
72
|
+
gateRegions.push({ pd, content: content as ContentBlock[], gateIdx });
|
|
73
|
+
});
|
|
80
74
|
}
|
|
81
75
|
return { agreementUuid, gateRegions };
|
|
82
76
|
}
|
|
@@ -219,21 +213,12 @@ export function seedSigningBlocks(
|
|
|
219
213
|
orgName: session.orgName,
|
|
220
214
|
certificate: session.certificate,
|
|
221
215
|
};
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const props = block?.props;
|
|
227
|
-
if (!props) return;
|
|
228
|
-
for (const propVal of Object.values(props)) {
|
|
229
|
-
if (!Array.isArray(propVal)) continue;
|
|
230
|
-
for (const child of propVal) {
|
|
231
|
-
if (child && typeof child === "object") seedBlock(child as ContentBlock);
|
|
216
|
+
for (const pd of regionTrees(resolved)) {
|
|
217
|
+
for (const block of collectBlocks(pd)) {
|
|
218
|
+
if (SIGNING_BLOCK_TYPES.has(block.type)) {
|
|
219
|
+
(block as ContentBlock).props = { ...((block.props as Record<string, unknown>) ?? {}), ...seed };
|
|
232
220
|
}
|
|
233
221
|
}
|
|
234
|
-
};
|
|
235
|
-
for (const { content } of regionContents(resolved)) {
|
|
236
|
-
for (const block of content) seedBlock(block);
|
|
237
222
|
}
|
|
238
223
|
}
|
|
239
224
|
|
package/src/agreement-gate.ts
CHANGED
|
@@ -132,8 +132,8 @@ export const agreementGateConfig: ContentGateConfig = {
|
|
|
132
132
|
|
|
133
133
|
if (hasPrePlacedGate(resolved)) {
|
|
134
134
|
const { gateRegions } = findAgreementGate(resolved);
|
|
135
|
-
for (const {
|
|
136
|
-
|
|
135
|
+
for (const { content, gateIdx } of gateRegions) {
|
|
136
|
+
content.splice(gateIdx, 1);
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
},
|
package/src/expand-roster.ts
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* @status handwritten
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { forEachContent, type PuckData } from "@teamscala/puck-tree/collect-blocks";
|
|
7
|
+
|
|
6
8
|
export interface RosterItem {
|
|
7
9
|
key: string;
|
|
8
10
|
role: string;
|
|
@@ -64,17 +66,6 @@ function substitute(
|
|
|
64
66
|
);
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
function slotArrays(block: Block): unknown[][] {
|
|
68
|
-
const out: unknown[][] = [];
|
|
69
|
-
const props = block.props;
|
|
70
|
-
if (!props) return out;
|
|
71
|
-
for (const v of Object.values(props)) {
|
|
72
|
-
if (Array.isArray(v) && v.every((e) => e && typeof e === "object")) {
|
|
73
|
-
out.push(v as unknown[]);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return out;
|
|
77
|
-
}
|
|
78
69
|
|
|
79
70
|
function cloneForItem(template: Block, item: RosterItem): Block {
|
|
80
71
|
const labels = {
|
|
@@ -119,23 +110,22 @@ function isPartyTemplate(block: Block): boolean {
|
|
|
119
110
|
return false;
|
|
120
111
|
}
|
|
121
112
|
|
|
122
|
-
function
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
for (const
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
} else {
|
|
135
|
-
out.push(block);
|
|
113
|
+
function expandTree(tree: PuckData, parties: RosterItem[]): void {
|
|
114
|
+
const arrays: unknown[][] = [];
|
|
115
|
+
forEachContent(tree, (content) => arrays.push(content));
|
|
116
|
+
for (const content of arrays.reverse()) {
|
|
117
|
+
const expanded: unknown[] = [];
|
|
118
|
+
for (const raw of content) {
|
|
119
|
+
const block = raw as Block;
|
|
120
|
+
if (isPartyTemplate(block)) {
|
|
121
|
+
for (const party of parties) expanded.push(cloneForItem(block, party));
|
|
122
|
+
} else {
|
|
123
|
+
expanded.push(block);
|
|
124
|
+
}
|
|
136
125
|
}
|
|
126
|
+
content.length = 0;
|
|
127
|
+
content.push(...expanded);
|
|
137
128
|
}
|
|
138
|
-
return out;
|
|
139
129
|
}
|
|
140
130
|
|
|
141
131
|
export interface RosterMeta {
|
|
@@ -167,7 +157,7 @@ export function expandRosterTemplates(
|
|
|
167
157
|
const pd = (value as { puck_data?: { content?: unknown[] } } | null)
|
|
168
158
|
?.puck_data;
|
|
169
159
|
if (!pd || !Array.isArray(pd.content)) continue;
|
|
170
|
-
if (parties.length > 0) pd
|
|
160
|
+
if (parties.length > 0) expandTree(pd as PuckData, parties);
|
|
171
161
|
if (meta) substituteMeta(pd.content, meta);
|
|
172
162
|
}
|
|
173
163
|
}
|
package/src/pg-boss-mount.ts
CHANGED
|
@@ -10,16 +10,15 @@ import { getAllEntries } from "@teamscala/db/registry/accessors";
|
|
|
10
10
|
import type { RegistryEntry } from "@teamscala/db/registry/types";
|
|
11
11
|
import { recordEvent } from "@teamscala/event-log/record-event";
|
|
12
12
|
import { getAppLogger } from "@teamscala/logger/app-loggers";
|
|
13
|
+
import type { JobHandler } from "@teamscala/os/runtime-contracts/mount-context";
|
|
13
14
|
import type { ScheduleOptions } from "pg-boss";
|
|
14
15
|
import { PgBoss } from "pg-boss";
|
|
15
|
-
import { pgBossHandlerRegistry } from "./pg-boss-handler-registry.ts";
|
|
16
|
-
|
|
17
|
-
|
|
18
16
|
|
|
19
17
|
export interface PgBossMountContext {
|
|
20
18
|
databaseUrl: string;
|
|
21
19
|
serviceSlug: string;
|
|
22
20
|
packages: string[];
|
|
21
|
+
jobHandlers: ReadonlyMap<string, JobHandler>;
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
export interface MountedJob {
|
|
@@ -62,18 +61,20 @@ export function intervalToCron(intervalSeconds: number): string {
|
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
async function resolveHandler(
|
|
64
|
+
boss: PgBoss,
|
|
65
|
+
jobHandlers: ReadonlyMap<string, JobHandler>,
|
|
65
66
|
handlerSlug: string,
|
|
66
67
|
config: ScheduledJobConfig,
|
|
67
68
|
jobData?: unknown,
|
|
68
69
|
): Promise<unknown> {
|
|
69
|
-
const handler =
|
|
70
|
+
const handler = jobHandlers.get(handlerSlug);
|
|
70
71
|
if (!handler) {
|
|
71
72
|
throw new Error(
|
|
72
73
|
`pg-boss handler "${handlerSlug}" has no registered function — ` +
|
|
73
|
-
`register it
|
|
74
|
+
`register it with ctx.registerJobHandler(slug, fn) in its package mount`,
|
|
74
75
|
);
|
|
75
76
|
}
|
|
76
|
-
return await handler({ config, jobData });
|
|
77
|
+
return await handler({ config, jobData, sendJob: (queue, data) => boss.send(queue, data) });
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
export async function mountPgBoss(
|
|
@@ -146,7 +147,7 @@ export async function mountPgBoss(
|
|
|
146
147
|
await boss.work(queueName, async (jobs) => {
|
|
147
148
|
let result: unknown;
|
|
148
149
|
for (const job of jobs) {
|
|
149
|
-
result = await resolveHandler(config.handler_slug, config, job.data);
|
|
150
|
+
result = await resolveHandler(boss, ctx.jobHandlers, config.handler_slug, config, job.data);
|
|
150
151
|
}
|
|
151
152
|
return jobs.length === 1 ? result : undefined;
|
|
152
153
|
});
|
|
@@ -4,28 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { getAppLogger } from "@teamscala/logger/app-loggers";
|
|
7
|
+
import { collectBlockTypes, type PuckData } from "@teamscala/puck-tree/collect-blocks";
|
|
7
8
|
import type { RendererComponentEntry } from "../types.ts";
|
|
8
9
|
|
|
9
10
|
const uncoveredBlocksLogged = new Set<string>();
|
|
10
11
|
|
|
11
|
-
function collectBlockTypes(node: unknown, out: Set<string>): void {
|
|
12
|
-
if (Array.isArray(node)) {
|
|
13
|
-
for (const child of node) collectBlockTypes(child, out);
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
if (node && typeof node === "object") {
|
|
17
|
-
const obj = node as Record<string, unknown>;
|
|
18
|
-
if (
|
|
19
|
-
typeof obj.type === "string" &&
|
|
20
|
-
obj.props &&
|
|
21
|
-
typeof obj.props === "object"
|
|
22
|
-
) {
|
|
23
|
-
out.add(obj.type);
|
|
24
|
-
}
|
|
25
|
-
for (const value of Object.values(obj)) collectBlockTypes(value, out);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
12
|
export function warnUncoveredBlocks(
|
|
30
13
|
pageData: unknown,
|
|
31
14
|
components: Record<string, RendererComponentEntry>,
|
|
@@ -33,8 +16,8 @@ export function warnUncoveredBlocks(
|
|
|
33
16
|
path: unknown,
|
|
34
17
|
log: ReturnType<typeof getAppLogger>,
|
|
35
18
|
): void {
|
|
36
|
-
const
|
|
37
|
-
collectBlockTypes(
|
|
19
|
+
const pd = pageData as { puckData?: PuckData; regions?: Array<{ puckData?: PuckData }> };
|
|
20
|
+
const used = collectBlockTypes([pd.puckData, ...(pd.regions ?? []).map((r) => r.puckData)]);
|
|
38
21
|
for (const type of used) {
|
|
39
22
|
if (type in components) continue;
|
|
40
23
|
const key = `${serviceSlug}:${type}`;
|
|
@@ -46,6 +46,7 @@ export async function buildFullApp(
|
|
|
46
46
|
databaseUrl: minimalResult.databaseUrl,
|
|
47
47
|
serviceSlug,
|
|
48
48
|
packages: config.server_packages ?? [],
|
|
49
|
+
jobHandlers: mountRegistry.jobHandlers,
|
|
49
50
|
}));
|
|
50
51
|
log.info(`[two-phase-boot:${serviceSlug}] phase-2 step: mountPgBoss DONE (${pgBossJobs.length} jobs)`);
|
|
51
52
|
} catch (error) {
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @system service-boot
|
|
3
|
-
* @status handwritten
|
|
4
|
-
*/
|
|
5
|
-
import type { ScheduledJobConfig } from "@teamscala/db-validation/registry-schemas/scheduled-job";
|
|
6
|
-
import { createRegistry } from "@teamscala/generic-registry/create-registry";
|
|
7
|
-
|
|
8
|
-
export type PgBossJobHandler = (ctx: {
|
|
9
|
-
config: ScheduledJobConfig;
|
|
10
|
-
jobData?: unknown;
|
|
11
|
-
}) => Promise<unknown> | unknown;
|
|
12
|
-
|
|
13
|
-
export const pgBossHandlerRegistry = createRegistry<PgBossJobHandler>(
|
|
14
|
-
"pg-boss-handlers",
|
|
15
|
-
);
|