@starci/hfs 5.0.7 → 5.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/CHANGELOG.md +21 -0
- package/README.md +7 -8
- package/cache/fake-session.mjs +1 -1
- package/cache/hfs-family.mjs +22 -9
- package/cache/import-graph.d.mts +32 -4
- package/cache/import-graph.mjs +88 -11
- package/cache/lint-family.d.mts +10 -3
- package/cache/lint-family.mjs +65 -37
- package/cache/records.d.mts +7 -0
- package/cache/records.mjs +17 -1
- package/cache/run.d.mts +1 -1
- package/cache/session.mjs +6 -2
- package/cache/side-key.d.mts +2 -0
- package/cache/side-key.mjs +5 -1
- package/cache/structure-memo.d.mts +34 -0
- package/cache/structure-memo.mjs +150 -0
- package/cache/types.d.mts +6 -2
- package/cache/wiring-family.mjs +1 -1
- package/codemod/directive-first.d.mts +48 -0
- package/codemod/directive-first.mjs +136 -0
- package/codemod/explicit-surface-edit.d.mts +14 -0
- package/codemod/explicit-surface-edit.mjs +57 -0
- package/codemod/explicit-surface-names.d.mts +30 -0
- package/codemod/explicit-surface-names.mjs +135 -0
- package/codemod/explicit-surface-render.d.mts +29 -0
- package/codemod/explicit-surface-render.mjs +197 -0
- package/codemod/explicit-surface.d.mts +50 -0
- package/codemod/explicit-surface.mjs +374 -0
- package/codemod/main.d.mts +7 -5
- package/codemod/main.mjs +37 -16
- package/codemod/small-renames.mjs +22 -2
- package/codemod/source-tree.d.mts +8 -0
- package/codemod/source-tree.mjs +23 -0
- package/lint/eslint-list.cjs +55 -0
- package/lint/run.d.mts +35 -61
- package/lint/run.mjs +91 -288
- package/package.json +1 -1
- package/release/lint-report.d.mts +1 -1
- package/release/lint-report.mjs +3 -3
- package/report/sonar.d.mts +1 -1
- package/report/sonar.mjs +3 -3
- package/report/stylelint-findings.d.mts +23 -0
- package/report/stylelint-findings.mjs +9 -0
- package/runtime/knowledge/hfs/canon-pins.yaml +8 -8
- package/runtime/knowledge/hfs/ci-standard.yaml +2 -2
- package/runtime/knowledge/hfs/rules.yaml +56 -5
- package/runtime/knowledge/hfs/slots.yaml +1 -1
- package/runtime/knowledge/sonar-gate.yaml +2 -2
- package/runtime/modules/kernel/failure-codes.yaml +4 -4
- package/runtime/scripts/api/node/run-node.d.mts +5 -0
- package/runtime/scripts/hfs/architecture/test-world-files.d.mts +4 -3
- package/runtime/scripts/hfs/architecture/test-world-files.mjs +26 -7
- package/runtime/scripts/hfs/check.d.mts +16 -1
- package/runtime/scripts/hfs/check.mjs +18 -7
- package/runtime/scripts/hfs/explicit-surface-rule.d.mts +58 -0
- package/runtime/scripts/hfs/explicit-surface-rule.mjs +59 -0
- package/runtime/scripts/hfs/gate-threshold.mjs +1 -1
- package/runtime/scripts/hfs/rule-memo.d.mts +30 -0
- package/runtime/scripts/hfs/rule-memo.mjs +24 -0
- package/runtime/scripts/hfs/rules/a15-findings.d.mts +6 -0
- package/runtime/scripts/hfs/rules/a15-findings.mjs +11 -2
- package/runtime/scripts/hfs/rules/ci-standard.mjs +2 -2
- package/runtime/scripts/hfs/rules/monorepo.d.mts +1 -3
- package/runtime/scripts/hfs/rules/monorepo.mjs +9 -11
- package/runtime/scripts/hfs/rules/pipeline.mjs +13 -9
- package/runtime/scripts/hfs/rules/read.d.mts +5 -0
- package/runtime/scripts/hfs/rules/read.mjs +13 -0
- package/runtime/scripts/hfs/rules/secrets.d.mts +2 -0
- package/runtime/scripts/hfs/rules/secrets.mjs +4 -1
- package/runtime/scripts/hfs/rules/stacks-release.mjs +35 -21
- package/runtime/scripts/lib/declaration-surface.d.mts +35 -0
- package/runtime/scripts/lib/declaration-surface.mjs +112 -0
- package/runtime/scripts/lib/worktree-exclude.mjs +1 -1
- package/scaffold/app.mjs +3 -3
- package/src/check-cache-structure-support.d.mts +52 -0
- package/src/main.d.mts +3 -2
- package/src/main.mjs +123 -109
- package/sync/ci-commands.d.mts +2 -1
- package/sync/ci-commands.mjs +19 -20
- package/sync/index.mjs +2 -2
- package/templates/app/package-scripts/core.json +2 -2
- package/templates/app/task-graph/turbo.json +0 -3
- package/templates/be/inbox/delivery-recorded.event.ts.tpl +2 -2
- package/templates/be/inbox/entities.index.ts.tpl +1 -1
- package/templates/be/inbox/module-definition.ts.tpl +8 -2
- package/templates/be/inbox/platform/event-bus/event-bus.module-definition.ts.tpl +10 -2
- package/templates/be/inbox/platform/inbox/inbox.module-definition.ts.tpl +8 -2
- package/templates/be/patterns/api/feature.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/api/graphql.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/cli/group.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/event-bus/platform/event-bus.module-definition.ts.tpl +8 -3
- package/templates/be/patterns/event-bus/platform/persistence/connection.ts.tpl +3 -2
- package/templates/be/patterns/event-bus/platform/persistence/event-bus.sql.ts.tpl +4 -3
- package/templates/be/patterns/idempotency/platform/inbox/inbox.module-definition.ts.tpl +8 -3
- package/templates/be/patterns/idempotency/platform/inbox/persistence/connection.ts.tpl +3 -2
- package/templates/be/patterns/idempotency/platform/inbox/persistence/inbox.sql.ts.tpl +7 -6
- package/templates/be/patterns/jobs/job.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/jobs/job.processor.ts.tpl +1 -1
- package/templates/be/patterns/jobs/job.queue.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/jobs/platform/jobs.module-definition.ts.tpl +8 -3
- package/templates/be/patterns/jobs/platform/persistence.connection.ts.tpl +3 -2
- package/templates/be/patterns/jobs/platform/persistence.jobs.sql.ts.tpl +5 -4
- package/templates/be/patterns/projections/persistence.connection.ts.tpl +3 -2
- package/templates/be/patterns/projections/projection.module-definition.ts.tpl +8 -3
- package/templates/be/patterns/queues/platform/persistence.connection.ts.tpl +3 -2
- package/templates/be/patterns/queues/platform/persistence.queue.sql.ts.tpl +4 -3
- package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +8 -3
- package/templates/be/patterns/queues/queue.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/reactors/consumer.ts.tpl +1 -1
- package/templates/be/patterns/reactors/message.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/reactors/reactor.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/realtime/channel.graphql.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/realtime/channel.sse.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/realtime/channel.websocket.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/saga/saga.done.consumer.ts.tpl +1 -1
- package/templates/be/patterns/saga/saga.failed.consumer.ts.tpl +1 -1
- package/templates/be/patterns/saga/saga.message.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/saga/saga.module-definition.ts.tpl +9 -3
- package/templates/be/patterns/webhooks/webhook.http.module-definition.ts.tpl +9 -3
- package/templates/be/platform/graphql/graphql.module-definition.ts +8 -3
- package/templates/be/skeleton/src/features/api/system-health/system-health.module-definition.ts +9 -3
- package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module-definition.ts +9 -3
- package/templates/be/skeleton/src/features/cli/cli.module-definition.ts +9 -3
- package/templates/be/skeleton/src/features/cli/migrate/migrate.module-definition.ts +9 -3
- package/templates/be/skeleton/src/features/cli/seed/seed.module-definition.ts +9 -3
- package/templates/be/skeleton/src/modules/domain/identity/identity.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/domain/liveness/liveness.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/domain/note/note.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/domain/note/persistence/connection.ts +3 -2
- package/templates/be/skeleton/src/modules/domain/note/persistence/note.sql.ts +2 -1
- package/templates/be/skeleton/src/modules/platform/clock/clock.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/platform/database/database.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/platform/database/index.ts +1 -0
- package/templates/be/skeleton/src/modules/platform/errors/errors.module-definition.ts +10 -4
- package/templates/be/skeleton/src/modules/platform/http/http.module-definition.ts +8 -4
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.module-definition.ts +10 -4
- package/templates/be/skeleton/src/modules/platform/http-security/persistence/connection.ts +3 -2
- package/templates/be/skeleton/src/modules/platform/http-security/persistence/rate-limit.sql.ts +3 -2
- package/templates/be/skeleton/src/modules/platform/i18n/i18n.module-definition.ts +10 -4
- package/templates/be/skeleton/src/modules/platform/ids/ids.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/platform/logging/logging.module-definition.ts +8 -3
- package/templates/be/skeleton/src/modules/platform/retry/retry.module-definition.ts +8 -3
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +1 -1
- package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/google.strategy.ts +5 -1
- package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/local.strategy.ts +5 -1
- package/templates/be/skeleton-lite/src/modules/platform/database/database.port.ts +1 -1
- package/templates/be/skeleton-lite/src/modules/platform/http-security/persistence/entities/index.ts +1 -1
- package/templates/be/skeleton-lite/src/tests/fixtures/builders/api.builder.ts +7 -1
- package/templates/be/skeleton-lite/src/tests/fixtures/builders/cli.builder.ts +7 -1
- package/templates/be/table/api/feature.module-definition.ts.tpl +9 -3
- package/templates/be/table/api/graphql.module-definition.ts.tpl +9 -3
- package/templates/be/table/entities.index.ts.tpl +1 -1
- package/templates/be/table/module-definition.ts.tpl +8 -3
- package/templates/be/test-world/fixtures/migrations.sql.ts.tpl +1 -0
- package/templates/be/test-world/world/test-world.config.ts.tpl +8 -1
- package/templates/fe/common/app-layout.tsx.partial +1 -1
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSession.ts +2 -1
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSignIn.ts +11 -3
- package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSignOut.ts +8 -2
- package/templates/fe/skeleton/apps/__app__/src/hooks/zustand/appGuide/store.ts +2 -1
- package/templates/fe/skeleton/apps/__app__/src/modules/toast/hooks.ts +1 -1
- package/templates/fe/skeleton/apps/__app__/src/modules/toast/toast.tsx +4 -1
- package/templates/fe/skeleton/apps/__app__/src/redux/hooks.ts +3 -2
- package/templates/fe/skeleton/apps/__app__/src/redux/index.ts +1 -0
- package/templates/fe/skeleton/apps/__app__/src/redux/slices/session.ts +12 -4
- package/templates/fe/skeleton/apps/__app__/src/redux/store.ts +8 -3
- package/templates/fe/skeleton/apps/app/src/app/[locale]/layout.tsx +4 -3
- package/templates/fe/skeleton/apps/app/src/features/layouts/AppLayout/index.tsx +1 -1
- package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/index.tsx +2 -1
- package/templates/fe/skeleton/apps/app/src/features/pages/AppLoadingPage/index.tsx +2 -1
- package/templates/fe/skeleton/apps/app/src/features/pages/AppNotFoundPage/index.tsx +2 -1
- package/templates/fe/skeleton/apps/app/src/modules/i18n/index.ts +2 -1
- package/templates/fe/skeleton/apps/app/src/modules/i18n/request.ts +4 -1
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/layout.tsx +4 -3
- package/templates/fe/skeleton/apps/landing/src/features/layouts/LandingLayout/index.tsx +2 -2
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingLoadingPage/index.tsx +2 -1
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingNotFoundPage/index.tsx +2 -1
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/index.tsx +2 -1
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/index.ts +2 -1
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/request.ts +4 -1
- package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +19 -1
- package/templates/fe/skeleton/packages/__project__-i18n/src/proxy.ts +1 -1
- package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +5 -1
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +4 -3
- package/templates/fe/skeleton-lite/apps/web/src/features/layouts/AppLayout/index.tsx +2 -2
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +2 -1
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppLoadingPage/index.tsx +2 -1
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppNotFoundPage/index.tsx +2 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +3 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +5 -1
- package/templates/fe/table/graphql/useMutateSwr.ts.tpl +2 -1
- package/templates/fe/table/graphql/useQuerySwr.ts.tpl +2 -1
- package/templates/fe/transport/graphql/clients/clients/auth.ts +1 -1
- package/templates/fe/transport/graphql/clients/clients/no-auth.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/attach-access-token.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/error.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/http.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/locale.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/refresh-token.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/retry.ts +1 -1
- package/templates/fe/transport/graphql/clients/links/timeout.ts +1 -1
- package/templates/fe/units/page/index.tsx.tpl +3 -2
- package/templates/fe/units/slot/SlotView.tsx.tpl +1 -1
package/sync/ci-commands.mjs
CHANGED
|
@@ -179,7 +179,7 @@ function ciJobSettings(declaration, source) {
|
|
|
179
179
|
};
|
|
180
180
|
}
|
|
181
181
|
/** An omitted quality declaration preserves hosted Sonar, required OIDC Codecov, and the high production dependency audit. */
|
|
182
|
-
export function ciWorkflowCommands(source, lcovReport = 'be/coverage/lcov.info', quality = undefined, { nodeMajor = '22', app } = {}) {
|
|
182
|
+
export function ciWorkflowCommands(source, lcovReport = 'be/coverage/lcov.info', quality = undefined, { nodeMajor = '22', app, packages = false } = {}) {
|
|
183
183
|
const configuredSonar = quality === undefined ? { token: 'SONAR_TOKEN', hostVar: 'SONAR_HOST_URL' } : quality.sonar;
|
|
184
184
|
const sonar = configuredSonar?.wiring === 'not-used' || configuredSonar?.mode === 'disabled' ? null : configuredSonar;
|
|
185
185
|
const codecov = quality?.codecov ?? { enabled: true, auth: 'oidc' };
|
|
@@ -195,8 +195,6 @@ export function ciWorkflowCommands(source, lcovReport = 'be/coverage/lcov.info',
|
|
|
195
195
|
shardCoverageReport: lcovReport.split('/').slice(0, -1).concat('coverage-final.json').join('/'),
|
|
196
196
|
ciBuildEnv: build.env,
|
|
197
197
|
ciBuildSteps: build.steps,
|
|
198
|
-
ciLint: exampleCommand(source, 'npm run lint'),
|
|
199
|
-
ciLintSonar: exampleCommand(source, 'npm run lint -- --out reports/lint.sonar.json'),
|
|
200
198
|
// The coverage judgement alone over the merged report: the fast and lint jobs already ran every other rule of the app check.
|
|
201
199
|
ciCoverageProof: exampleCommand(source, 'npx starci app coverage verify ' + lcovReport),
|
|
202
200
|
// An app with an entity list also checks its entities against the schema its migrations build, in the build job (a runner with Docker).
|
|
@@ -205,23 +203,24 @@ export function ciWorkflowCommands(source, lcovReport = 'be/coverage/lcov.info',
|
|
|
205
203
|
sonarTokenSecret: sonar?.token ?? 'SONAR_TOKEN',
|
|
206
204
|
sonarHostVar: sonar?.hostVar ?? 'SONAR_HOST_URL',
|
|
207
205
|
// One lint job per side (the lint matrix): with Sonar each side writes its own report, uploaded as sonar-lint-<side> and merged by the sonar job.
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
206
|
+
// The typed lint reads the generated code and the types the fe packages declare (their dist), so both are built first, the way the root lint script does.
|
|
207
|
+
ciLintSteps: [' - name: build the lint inputs', ` run: ${exampleCommand(source, packages ? 'npm run codegen --silent && npx --no-install turbo run build --filter=./fe/packages/*' : 'npm run codegen --silent')}`, ...(sonar ? [
|
|
208
|
+
' - name: lint with Sonar import',
|
|
209
|
+
` run: ${exampleCommand(source, 'npx --no-install starci app check --gate lint --side ' + side + ' --out reports/lint.' + side + '.sonar.json')}`,
|
|
210
|
+
' env:',
|
|
211
|
+
` NODE_OPTIONS: ${settings.nodeOptions}`,
|
|
212
|
+
` - uses: ${UPLOAD_ARTIFACT}`,
|
|
213
|
+
// Inside an if: expression the matrix value is an argument of format(), never a nested ${{ }} (actionlint: a nested expression makes the condition always true).
|
|
214
|
+
" if: always() && hashFiles(format('reports/lint.{0}.sonar.json', matrix.side)) != ''",
|
|
215
|
+
' with:',
|
|
216
|
+
` name: sonar-lint-${side}`,
|
|
217
|
+
` path: reports/lint.${side}.sonar.json`,
|
|
218
|
+
] : [
|
|
219
|
+
' - name: lint',
|
|
220
|
+
` run: ${exampleCommand(source, 'npx --no-install starci app check --gate lint --side ' + side)}`,
|
|
221
|
+
' env:',
|
|
222
|
+
` NODE_OPTIONS: ${settings.nodeOptions}`,
|
|
223
|
+
])].join('\n'),
|
|
225
224
|
codecovSteps: codecovSteps(lcovReport, codecov),
|
|
226
225
|
verdictSonarNeed: sonar ? ', sonar' : '',
|
|
227
226
|
verdictSonarCheck: sonar ? `\n if [[ "$FULL" == true && "$CHECKS" == true ]]; then expect sonar success; else expect sonar idle; fi` : '',
|
package/sync/index.mjs
CHANGED
|
@@ -404,7 +404,7 @@ export function variables(app, scope, presets, sonarKey, manifest = loadSlotMani
|
|
|
404
404
|
buildFe: 'npm run codegen --silent && turbo run build --filter=./fe/apps/*',
|
|
405
405
|
// The be side with its tsc; every fe workspace with its own typecheck script through turbo (the packages are built first: ^build).
|
|
406
406
|
typecheck: 'npm run codegen --silent && tsc -p be/tsconfig.json && turbo run typecheck',
|
|
407
|
-
// The typed lint reads the types the fe packages declare (their dist
|
|
407
|
+
// The typed lint reads the types the fe packages declare (their dist), so the packages are built first.
|
|
408
408
|
lintPrefix: packages ? 'npm run codegen --silent && turbo run build --filter=./fe/packages/* && ' : 'npm run codegen --silent && ',
|
|
409
409
|
nodeMajor: String(NODE_MAJOR),
|
|
410
410
|
// The deploy server's age identity the restore workflow writes (stack/server-key.mjs owns the path).
|
|
@@ -418,7 +418,7 @@ export function variables(app, scope, presets, sonarKey, manifest = loadSlotMani
|
|
|
418
418
|
sonarSources: ['be/apps', 'be/src', 'fe/apps', ...(packages ? ['fe/packages'] : [])].join(','),
|
|
419
419
|
lcovReport: LCOV_REPORT,
|
|
420
420
|
cpdExclusions: SONAR_CPD_EXCLUSIONS.join(','),
|
|
421
|
-
...ciWorkflowCommands(source, LCOV_REPORT, source.qualityDeclared === false ? undefined : source.quality, { app }),
|
|
421
|
+
...ciWorkflowCommands(source, LCOV_REPORT, source.qualityDeclared === false ? undefined : source.quality, { app, packages }),
|
|
422
422
|
// The coverage scope is the same in both editions: lite measures exactly what full measures (ruling:R212/case-1, amended by ruling:R212/case-4).
|
|
423
423
|
jestCoverage: scope === 'be' ? jestCoverageSource(jestCoverage(manifest, providers)) : '',
|
|
424
424
|
coverageExclusions: scope === APP_SCOPE ? sonarCoverageExclusions(manifest, providers).join(',') : '',
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"codegen": "node scripts/codegen.mjs",
|
|
4
4
|
"codegen:graphql": "node scripts/codegen.mjs --graphql",
|
|
5
5
|
"typecheck": "{{typecheck}}",
|
|
6
|
-
"lint": "{{lintPrefix}}{{appBinary}} lint",
|
|
7
|
-
"lint:fix": "{{lintPrefix}}{{appBinary}} lint --fix",
|
|
6
|
+
"lint": "{{lintPrefix}}{{appBinary}} check --gate lint",
|
|
7
|
+
"lint:fix": "{{lintPrefix}}{{appBinary}} check --gate lint --fix",
|
|
8
8
|
"format": "prettier --write .",
|
|
9
9
|
"format:check": "prettier --check .",
|
|
@@ -10,10 +10,10 @@ export interface @@Provider@@DeliveryRecordedPayload {
|
|
|
10
10
|
|
|
11
11
|
/** Version 1 of `@@provider@@.delivery-recorded`, published once by the provider inbox intake. */
|
|
12
12
|
export class @@Provider@@DeliveryRecordedEvent extends BaseEvent {
|
|
13
|
-
static readonly eventName = "@@provider@@.delivery-recorded"
|
|
13
|
+
static readonly eventName: string = "@@provider@@.delivery-recorded"
|
|
14
14
|
static readonly version = 1
|
|
15
15
|
|
|
16
|
-
readonly eventName = @@Provider@@DeliveryRecordedEvent.eventName
|
|
16
|
+
readonly eventName: string = @@Provider@@DeliveryRecordedEvent.eventName
|
|
17
17
|
|
|
18
18
|
private constructor(
|
|
19
19
|
readonly eventId: string,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { @@Provider@@InboxEntity } from "./@@provider@@-inbox.entity"
|
|
2
2
|
|
|
3
3
|
/** The entities of the @@provider@@-inbox capability, named in PRIMARY_ENTITIES of platform/database/entities.ts. */
|
|
4
|
-
export const @@providerCamel@@InboxEntities = [@@Provider@@InboxEntity]
|
|
4
|
+
export const @@providerCamel@@InboxEntities: ReadonlyArray<typeof @@Provider@@InboxEntity> = [@@Provider@@InboxEntity]
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import { ConfigurableModuleBuilder } from "@nestjs/common"
|
|
1
|
+
import { ConfigurableModuleBuilder, type ConfigurableModuleHost } from "@nestjs/common"
|
|
2
2
|
import type { @@Provider@@InboxOptions } from "./@@provider@@-inbox.options"
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
const built = new ConfigurableModuleBuilder<@@Provider@@InboxOptions>()
|
|
5
5
|
.setExtras({ isGlobal: false }, (definition, extras) => ({ ...definition, global: extras.isGlobal }))
|
|
6
6
|
.build()
|
|
7
|
+
type Host = ConfigurableModuleHost<@@Provider@@InboxOptions, "register", "create", { isGlobal: boolean }>
|
|
8
|
+
|
|
9
|
+
/** The configurable-module class the module extends. */
|
|
10
|
+
export const ConfigurableModuleClass: Host["ConfigurableModuleClass"] = built.ConfigurableModuleClass
|
|
11
|
+
/** The options of a synchronous registration, for the register signature. */
|
|
12
|
+
export const OPTIONS_TYPE: Host["OPTIONS_TYPE"] = built.OPTIONS_TYPE
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
import { ConfigurableModuleBuilder } from "@nestjs/common"
|
|
1
|
+
import { ConfigurableModuleBuilder, type ConfigurableModuleHost } from "@nestjs/common"
|
|
2
2
|
import type { EventBusOptions } from "./event-bus.options"
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
const built = new ConfigurableModuleBuilder<EventBusOptions>()
|
|
5
5
|
.setExtras({ isGlobal: false }, (definition, extras) => ({ ...definition, global: extras.isGlobal }))
|
|
6
6
|
.build()
|
|
7
|
+
type Host = ConfigurableModuleHost<EventBusOptions, "register", "create", { isGlobal: boolean }>
|
|
8
|
+
|
|
9
|
+
/** The configurable-module class the module extends. */
|
|
10
|
+
export const ConfigurableModuleClass: Host["ConfigurableModuleClass"] = built.ConfigurableModuleClass
|
|
11
|
+
/** The injection token of the registered options. */
|
|
12
|
+
export const MODULE_OPTIONS_TOKEN: Host["MODULE_OPTIONS_TOKEN"] = built.MODULE_OPTIONS_TOKEN
|
|
13
|
+
/** The options of a synchronous registration, for the register signature. */
|
|
14
|
+
export const OPTIONS_TYPE: Host["OPTIONS_TYPE"] = built.OPTIONS_TYPE
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import { ConfigurableModuleBuilder } from "@nestjs/common"
|
|
1
|
+
import { ConfigurableModuleBuilder, type ConfigurableModuleHost } from "@nestjs/common"
|
|
2
2
|
import type { InboxOptions } from "./inbox.options"
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
const built = new ConfigurableModuleBuilder<InboxOptions>()
|
|
5
5
|
.setExtras({ isGlobal: false }, (definition, extras) => ({ ...definition, global: extras.isGlobal }))
|
|
6
6
|
.build()
|
|
7
|
+
type Host = ConfigurableModuleHost<InboxOptions, "register", "create", { isGlobal: boolean }>
|
|
8
|
+
|
|
9
|
+
/** The configurable-module class the module extends. */
|
|
10
|
+
export const ConfigurableModuleClass: Host["ConfigurableModuleClass"] = built.ConfigurableModuleClass
|
|
11
|
+
/** The options of a synchronous registration, for the register signature. */
|
|
12
|
+
export const OPTIONS_TYPE: Host["OPTIONS_TYPE"] = built.OPTIONS_TYPE
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import { ConfigurableModuleBuilder } from "@nestjs/common"
|
|
1
|
+
import { ConfigurableModuleBuilder, type ConfigurableModuleHost } from "@nestjs/common"
|
|
2
2
|
import type { @@Feature@@ApiOptions } from "./@@feature@@.options"
|
|
3
3
|
|
|
4
|
+
const built = new ConfigurableModuleBuilder<@@Feature@@ApiOptions>().setClassMethodName("register").build()
|
|
5
|
+
type Host = ConfigurableModuleHost<@@Feature@@ApiOptions, "register", "create", {}>
|
|
6
|
+
|
|
4
7
|
/** The configurable-module base of the @@feature@@ module. */
|
|
5
|
-
export const
|
|
6
|
-
|
|
8
|
+
export const ConfigurableModuleClass: Host["ConfigurableModuleClass"] = built.ConfigurableModuleClass
|
|
9
|
+
/** The injection token of the registered options. */
|
|
10
|
+
export const MODULE_OPTIONS_TOKEN: Host["MODULE_OPTIONS_TOKEN"] = built.MODULE_OPTIONS_TOKEN
|
|
11
|
+
/** The options of a synchronous registration, for the register signature. */
|
|
12
|
+
export const OPTIONS_TYPE: Host["OPTIONS_TYPE"] = built.OPTIONS_TYPE
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import { ConfigurableModuleBuilder } from "@nestjs/common"
|
|
1
|
+
import { ConfigurableModuleBuilder, type ConfigurableModuleHost } from "@nestjs/common"
|
|
2
2
|
import type { @@Feature@@GraphqlOptions } from "./@@feature@@-graphql.options"
|
|
3
3
|
|
|
4
|
+
const built = new ConfigurableModuleBuilder<@@Feature@@GraphqlOptions>().setClassMethodName("register").build()
|
|
5
|
+
type Host = ConfigurableModuleHost<@@Feature@@GraphqlOptions, "register", "create", {}>
|
|
6
|
+
|
|
4
7
|
/** The configurable-module base of the @@feature@@-graphql module. */
|
|
5
|
-
export const
|
|
6
|
-
|
|
8
|
+
export const ConfigurableModuleClass: Host["ConfigurableModuleClass"] = built.ConfigurableModuleClass
|
|
9
|
+
/** The injection token of the registered options. */
|
|
10
|
+
export const MODULE_OPTIONS_TOKEN: Host["MODULE_OPTIONS_TOKEN"] = built.MODULE_OPTIONS_TOKEN
|
|
11
|
+
/** The options of a synchronous registration, for the register signature. */
|
|
12
|
+
export const OPTIONS_TYPE: Host["OPTIONS_TYPE"] = built.OPTIONS_TYPE
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import { ConfigurableModuleBuilder } from "@nestjs/common"
|
|
1
|
+
import { ConfigurableModuleBuilder, type ConfigurableModuleHost } from "@nestjs/common"
|
|
2
2
|
import type { @@Group@@Options } from "./@@group@@.options"
|
|
3
3
|
|
|
4
|
+
const built = new ConfigurableModuleBuilder<@@Group@@Options>().setClassMethodName("register").build()
|
|
5
|
+
type Host = ConfigurableModuleHost<@@Group@@Options, "register", "create", {}>
|
|
6
|
+
|
|
4
7
|
/** The configurable-module base of the @@group@@ module. */
|
|
5
|
-
export const
|
|
6
|
-
|
|
8
|
+
export const ConfigurableModuleClass: Host["ConfigurableModuleClass"] = built.ConfigurableModuleClass
|
|
9
|
+
/** The injection token of the registered options. */
|
|
10
|
+
export const MODULE_OPTIONS_TOKEN: Host["MODULE_OPTIONS_TOKEN"] = built.MODULE_OPTIONS_TOKEN
|
|
11
|
+
/** The options of a synchronous registration, for the register signature. */
|
|
12
|
+
export const OPTIONS_TYPE: Host["OPTIONS_TYPE"] = built.OPTIONS_TYPE
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import { ConfigurableModuleBuilder } from "@nestjs/common"
|
|
1
|
+
import { ConfigurableModuleBuilder, type ConfigurableModuleHost } from "@nestjs/common"
|
|
2
2
|
import { EVENT_BUS_OPTIONS } from "./event-bus.decorators"
|
|
3
3
|
import type { EventBusOptions } from "./event-bus.options"
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
export const { ConfigurableModuleClass, OPTIONS_TYPE } = new ConfigurableModuleBuilder<EventBusOptions>({
|
|
5
|
+
const built = new ConfigurableModuleBuilder<EventBusOptions>({
|
|
7
6
|
optionsInjectionToken: EVENT_BUS_OPTIONS,
|
|
8
7
|
})
|
|
9
8
|
.setExtras({ isGlobal: false }, (definition, extras) => ({ ...definition, global: extras.isGlobal }))
|
|
10
9
|
.build()
|
|
10
|
+
type Host = ConfigurableModuleHost<EventBusOptions, "register", "create", { isGlobal: boolean }>
|
|
11
|
+
|
|
12
|
+
/** The configurable-module base of the event bus; `isGlobal` is decided by the app root. */
|
|
13
|
+
export const ConfigurableModuleClass: Host["ConfigurableModuleClass"] = built.ConfigurableModuleClass
|
|
14
|
+
/** The options of a synchronous registration, for the register signature. */
|
|
15
|
+
export const OPTIONS_TYPE: Host["OPTIONS_TYPE"] = built.OPTIONS_TYPE
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import type { DatabaseConnectionOptions } from "@modules/platform/database"
|
|
1
2
|
import { EventOutboxEntity } from "./entities/event-outbox.entity"
|
|
2
3
|
import { CreateEventOutbox@@epochMs13@@ } from "./migrations/@@epochMs13@@-create-event-outbox"
|
|
3
4
|
|
|
4
5
|
/** The entities of the event bus, for every connection that publishes events. */
|
|
5
|
-
export const eventBusEntities = [EventOutboxEntity]
|
|
6
|
+
export const eventBusEntities: DatabaseConnectionOptions["entities"] = [EventOutboxEntity]
|
|
6
7
|
|
|
7
8
|
/** The migrations of the event bus, in the order they run. */
|
|
8
|
-
export const eventBusMigrations = [CreateEventOutbox@@epochMs13@@]
|
|
9
|
+
export const eventBusMigrations: DatabaseConnectionOptions["migrations"] = [CreateEventOutbox@@epochMs13@@]
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { sql } from "@modules/platform/database"
|
|
2
|
+
import type { SqlText } from "@modules/platform/database"
|
|
2
3
|
|
|
3
4
|
/** Writes one event in the outbox ($1 event id, $2 event name, $3 topic, $4 key, $5 envelope, $6 instant, $7 correlation id). */
|
|
4
|
-
export const INSERT_OUTBOX_ROW = sql`INSERT INTO event_outbox (event_id, event_name, topic, message_key, envelope, created_at, correlation_id)
|
|
5
|
+
export const INSERT_OUTBOX_ROW: SqlText = sql`INSERT INTO event_outbox (event_id, event_name, topic, message_key, envelope, created_at, correlation_id)
|
|
5
6
|
VALUES ($1, $2, $3, $4, $5, $6, $7)`
|
|
6
7
|
|
|
7
8
|
/** Locks and reads the oldest rows that wait ($1 batch size); a second relay skips the rows this one holds. */
|
|
8
|
-
export const SELECT_WAITING_ROWS = sql`SELECT id, topic, message_key, envelope, correlation_id FROM event_outbox
|
|
9
|
+
export const SELECT_WAITING_ROWS: SqlText = sql`SELECT id, topic, message_key, envelope, correlation_id FROM event_outbox
|
|
9
10
|
WHERE sent_at IS NULL ORDER BY id LIMIT $1 FOR UPDATE SKIP LOCKED`
|
|
10
11
|
|
|
11
12
|
/** Marks the rows as handed to the broker ($1 ids, $2 instant). */
|
|
12
|
-
export const MARK_ROWS_SENT = sql`UPDATE event_outbox SET sent_at = $2 WHERE id = ANY($1::bigint[])`
|
|
13
|
+
export const MARK_ROWS_SENT: SqlText = sql`UPDATE event_outbox SET sent_at = $2 WHERE id = ANY($1::bigint[])`
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { ConfigurableModuleBuilder } from "@nestjs/common"
|
|
1
|
+
import { ConfigurableModuleBuilder, type ConfigurableModuleHost } from "@nestjs/common"
|
|
2
2
|
import type { InboxOptions } from "./inbox.options"
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
export const { ConfigurableModuleClass, OPTIONS_TYPE } = new ConfigurableModuleBuilder<InboxOptions>()
|
|
4
|
+
const built = new ConfigurableModuleBuilder<InboxOptions>()
|
|
6
5
|
.setExtras({ isGlobal: false }, (definition, extras) => ({ ...definition, global: extras.isGlobal }))
|
|
7
6
|
.build()
|
|
7
|
+
type Host = ConfigurableModuleHost<InboxOptions, "register", "create", { isGlobal: boolean }>
|
|
8
|
+
|
|
9
|
+
/** The configurable-module base of the inbox capability; `isGlobal` is decided by the app root. */
|
|
10
|
+
export const ConfigurableModuleClass: Host["ConfigurableModuleClass"] = built.ConfigurableModuleClass
|
|
11
|
+
/** The options of a synchronous registration, for the register signature. */
|
|
12
|
+
export const OPTIONS_TYPE: Host["OPTIONS_TYPE"] = built.OPTIONS_TYPE
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import type { DatabaseConnectionOptions } from "@modules/platform/database"
|
|
1
2
|
import { InboxClaimEntity } from "./entities/inbox-claim.entity"
|
|
2
3
|
import { CreateInboxClaims1789800005000 } from "./migrations/1789800005000-create-inbox-claims"
|
|
3
4
|
import { ClientOperationOutcomes1792800000000 } from "./migrations/1792800000000-client-operation-outcomes"
|
|
4
5
|
|
|
5
6
|
/** The entities of the inbox capability, for the connection that holds them. */
|
|
6
|
-
export const inboxEntities = [InboxClaimEntity]
|
|
7
|
+
export const inboxEntities: DatabaseConnectionOptions["entities"] = [InboxClaimEntity]
|
|
7
8
|
|
|
8
9
|
/** The migrations of the inbox capability, in the order they run. */
|
|
9
|
-
export const inboxMigrations = [CreateInboxClaims1789800005000, ClientOperationOutcomes1792800000000]
|
|
10
|
+
export const inboxMigrations: DatabaseConnectionOptions["migrations"] = [CreateInboxClaims1789800005000, ClientOperationOutcomes1792800000000]
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import { sql } from "@modules/platform/database"
|
|
2
|
+
import type { SqlText } from "@modules/platform/database"
|
|
2
3
|
|
|
3
4
|
/** Claims the event ($1 source, $2 event id, $3 instant); answers one row when this call won and none when it was claimed before. */
|
|
4
|
-
export const CLAIM_EVENT = sql`INSERT INTO inbox_claims (source, event_id, claimed_at) VALUES ($1, $2, $3)
|
|
5
|
+
export const CLAIM_EVENT: SqlText = sql`INSERT INTO inbox_claims (source, event_id, claimed_at) VALUES ($1, $2, $3)
|
|
5
6
|
ON CONFLICT (source, event_id) DO NOTHING RETURNING event_id`
|
|
6
7
|
|
|
7
8
|
/** Gives a delivery claim back; client outcomes can expire only through the retention API. */
|
|
8
|
-
export const RELEASE_EVENT = sql`DELETE FROM inbox_claims WHERE source = $1 AND event_id = $2 AND request_fingerprint IS NULL`
|
|
9
|
+
export const RELEASE_EVENT: SqlText = sql`DELETE FROM inbox_claims WHERE source = $1 AND event_id = $2 AND request_fingerprint IS NULL`
|
|
9
10
|
|
|
10
11
|
/** A conflicting insert locks the existing row and returns its committed fingerprint and outcome after the winner commits. */
|
|
11
|
-
export const CLAIM_CLIENT = sql`INSERT INTO inbox_claims (source, event_id, request_fingerprint, claimed_at)
|
|
12
|
+
export const CLAIM_CLIENT: SqlText = sql`INSERT INTO inbox_claims (source, event_id, request_fingerprint, claimed_at)
|
|
12
13
|
VALUES ($1, $2, $3, $4)
|
|
13
14
|
ON CONFLICT (source, event_id) DO UPDATE SET outcome = COALESCE(inbox_claims.outcome, '{}'::jsonb)
|
|
14
15
|
RETURNING request_fingerprint, outcome, (outcome IS NOT NULL AND (jsonb_typeof(outcome) <> 'object' OR NOT (outcome ? 'value'))) AS incomplete`
|
|
15
16
|
|
|
16
17
|
/** The JSON envelope distinguishes a completed null result from an uncompleted claim in this transaction. */
|
|
17
|
-
export const SAVE_CLIENT_OUTCOME = sql`WITH saved AS (
|
|
18
|
+
export const SAVE_CLIENT_OUTCOME: SqlText = sql`WITH saved AS (
|
|
18
19
|
UPDATE inbox_claims SET outcome = $3::jsonb
|
|
19
20
|
WHERE source = $1 AND event_id = $2
|
|
20
21
|
RETURNING outcome
|
|
@@ -22,7 +23,7 @@ export const SAVE_CLIENT_OUTCOME = sql`WITH saved AS (
|
|
|
22
23
|
SELECT outcome FROM saved LIMIT 1`
|
|
23
24
|
|
|
24
25
|
/** Retention never deletes delivery rows or an incomplete claim; its bounded batch skips rows held by active retries. */
|
|
25
|
-
export const SWEEP_CLIENTS = sql`WITH expired AS (
|
|
26
|
+
export const SWEEP_CLIENTS: SqlText = sql`WITH expired AS (
|
|
26
27
|
SELECT source, event_id FROM inbox_claims
|
|
27
28
|
WHERE source LIKE 'client:%' AND request_fingerprint IS NOT NULL AND outcome IS NOT NULL AND jsonb_typeof(outcome) = 'object' AND outcome ? 'value' AND claimed_at < $1
|
|
28
29
|
ORDER BY claimed_at, source, event_id LIMIT $2 FOR UPDATE SKIP LOCKED
|
|
@@ -34,6 +35,6 @@ RETURNING claims.event_id
|
|
|
34
35
|
SELECT count(*)::int AS deleted_count FROM deleted`
|
|
35
36
|
|
|
36
37
|
/** Imports an already completed legacy operation without replacing a key another migration has already transferred. */
|
|
37
|
-
export const BACKFILL_CLIENT_OUTCOME = sql`INSERT INTO inbox_claims (source, event_id, request_fingerprint, claimed_at, outcome)
|
|
38
|
+
export const BACKFILL_CLIENT_OUTCOME: SqlText = sql`INSERT INTO inbox_claims (source, event_id, request_fingerprint, claimed_at, outcome)
|
|
38
39
|
VALUES ($1, $2, $3, $4, $5::jsonb)
|
|
39
40
|
ON CONFLICT (source, event_id) DO NOTHING`
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import { ConfigurableModuleBuilder } from "@nestjs/common"
|
|
1
|
+
import { ConfigurableModuleBuilder, type ConfigurableModuleHost } from "@nestjs/common"
|
|
2
2
|
import type { @@Job@@Options } from "./@@job@@.options"
|
|
3
3
|
|
|
4
|
+
const built = new ConfigurableModuleBuilder<@@Job@@Options>().setClassMethodName("register").build()
|
|
5
|
+
type Host = ConfigurableModuleHost<@@Job@@Options, "register", "create", {}>
|
|
6
|
+
|
|
4
7
|
/** The configurable-module base of the @@job@@ module. */
|
|
5
|
-
export const
|
|
6
|
-
|
|
8
|
+
export const ConfigurableModuleClass: Host["ConfigurableModuleClass"] = built.ConfigurableModuleClass
|
|
9
|
+
/** The injection token of the registered options. */
|
|
10
|
+
export const MODULE_OPTIONS_TOKEN: Host["MODULE_OPTIONS_TOKEN"] = built.MODULE_OPTIONS_TOKEN
|
|
11
|
+
/** The options of a synchronous registration, for the register signature. */
|
|
12
|
+
export const OPTIONS_TYPE: Host["OPTIONS_TYPE"] = built.OPTIONS_TYPE
|
|
@@ -7,7 +7,7 @@ import { @@Step@@Step } from "../../steps/@@step@@.step"
|
|
|
7
7
|
@Injectable()
|
|
8
8
|
/** The processor of the @@job@@ job: the runner claims and settles, `process` runs the steps in order. */
|
|
9
9
|
export class @@Job@@Processor extends FencedProcessor {
|
|
10
|
-
readonly queue = @@jobUpper@@_QUEUE
|
|
10
|
+
readonly queue: string = @@jobUpper@@_QUEUE
|
|
11
11
|
|
|
12
12
|
constructor(private readonly @@stepCamel@@Step: @@Step@@Step) {
|
|
13
13
|
super()
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import { ConfigurableModuleBuilder } from "@nestjs/common"
|
|
1
|
+
import { ConfigurableModuleBuilder, type ConfigurableModuleHost } from "@nestjs/common"
|
|
2
2
|
import type { @@Job@@QueueOptions } from "./@@job@@-queue.options"
|
|
3
3
|
|
|
4
|
+
const built = new ConfigurableModuleBuilder<@@Job@@QueueOptions>().setClassMethodName("register").build()
|
|
5
|
+
type Host = ConfigurableModuleHost<@@Job@@QueueOptions, "register", "create", {}>
|
|
6
|
+
|
|
4
7
|
/** The configurable-module base of the @@job@@-queue module. */
|
|
5
|
-
export const
|
|
6
|
-
|
|
8
|
+
export const ConfigurableModuleClass: Host["ConfigurableModuleClass"] = built.ConfigurableModuleClass
|
|
9
|
+
/** The injection token of the registered options. */
|
|
10
|
+
export const MODULE_OPTIONS_TOKEN: Host["MODULE_OPTIONS_TOKEN"] = built.MODULE_OPTIONS_TOKEN
|
|
11
|
+
/** The options of a synchronous registration, for the register signature. */
|
|
12
|
+
export const OPTIONS_TYPE: Host["OPTIONS_TYPE"] = built.OPTIONS_TYPE
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import { ConfigurableModuleBuilder } from "@nestjs/common"
|
|
1
|
+
import { ConfigurableModuleBuilder, type ConfigurableModuleHost } from "@nestjs/common"
|
|
2
2
|
import { JOBS_OPTIONS } from "./jobs.decorators"
|
|
3
3
|
import type { JobsOptions } from "./jobs.options"
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
export const { ConfigurableModuleClass, OPTIONS_TYPE } = new ConfigurableModuleBuilder<JobsOptions>({
|
|
5
|
+
const built = new ConfigurableModuleBuilder<JobsOptions>({
|
|
7
6
|
optionsInjectionToken: JOBS_OPTIONS,
|
|
8
7
|
})
|
|
9
8
|
.setExtras({ isGlobal: false }, (definition, extras) => ({ ...definition, global: extras.isGlobal }))
|
|
10
9
|
.build()
|
|
10
|
+
type Host = ConfigurableModuleHost<JobsOptions, "register", "create", { isGlobal: boolean }>
|
|
11
|
+
|
|
12
|
+
/** The configurable-module base of the jobs; `isGlobal` is decided by the app root. */
|
|
13
|
+
export const ConfigurableModuleClass: Host["ConfigurableModuleClass"] = built.ConfigurableModuleClass
|
|
14
|
+
/** The options of a synchronous registration, for the register signature. */
|
|
15
|
+
export const OPTIONS_TYPE: Host["OPTIONS_TYPE"] = built.OPTIONS_TYPE
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import type { DatabaseConnectionOptions } from "@modules/platform/database"
|
|
1
2
|
import { JobEntity } from "./entities/job.entity"
|
|
2
3
|
import { CreateJobs@@epochMs13@@ } from "./migrations/@@epochMs13@@-create-jobs"
|
|
3
4
|
|
|
4
5
|
/** The entities of the jobs, for every connection that runs jobs. */
|
|
5
|
-
export const jobsEntities = [JobEntity]
|
|
6
|
+
export const jobsEntities: DatabaseConnectionOptions["entities"] = [JobEntity]
|
|
6
7
|
|
|
7
8
|
/** The migrations of the jobs, in the order they run. */
|
|
8
|
-
export const jobsMigrations = [CreateJobs@@epochMs13@@]
|
|
9
|
+
export const jobsMigrations: DatabaseConnectionOptions["migrations"] = [CreateJobs@@epochMs13@@]
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { sql } from "@modules/platform/database"
|
|
2
|
+
import type { SqlText } from "@modules/platform/database"
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Claims a delivery in one statement ($1 kind, $2 job key, $3 worker, $4 lease end, $5 payload, $6 instant). A new key inserts the row
|
|
5
6
|
* with token 1; an existing key is claimed again only when the job failed or its lease expired, and then the token is bumped by the
|
|
6
7
|
* store. A done job and a live claim match nothing, so no row comes back.
|
|
7
8
|
*/
|
|
8
|
-
export const CLAIM_JOB = sql`INSERT INTO jobs (kind, job_key, status, fencing_token, claimed_by, lease_expires_at, payload, created_at, updated_at)
|
|
9
|
+
export const CLAIM_JOB: SqlText = sql`INSERT INTO jobs (kind, job_key, status, fencing_token, claimed_by, lease_expires_at, payload, created_at, updated_at)
|
|
9
10
|
VALUES ($1, $2, 'running', 1, $3, $4, $5::jsonb, $6, $6)
|
|
10
11
|
ON CONFLICT (job_key) DO UPDATE
|
|
11
12
|
SET fencing_token = jobs.fencing_token + 1, claimed_by = $3, lease_expires_at = $4, status = 'running', error = NULL, updated_at = $6
|
|
@@ -15,19 +16,19 @@ export const CLAIM_JOB = sql`INSERT INTO jobs (kind, job_key, status, fencing_to
|
|
|
15
16
|
/** The guarded writes wrap their UPDATE in a CTE: TypeORM returns a bare `UPDATE ... RETURNING` as `[rows, count]`, a SELECT as rows. */
|
|
16
17
|
|
|
17
18
|
/** Records a step ($1 job id, $2 expected token, $3 step, $4 instant); no row comes back when the token is stale. */
|
|
18
|
-
export const ADVANCE_JOB = sql`WITH changed AS (UPDATE jobs SET current_step = $3, updated_at = $4
|
|
19
|
+
export const ADVANCE_JOB: SqlText = sql`WITH changed AS (UPDATE jobs SET current_step = $3, updated_at = $4
|
|
19
20
|
WHERE id = $1 AND fencing_token = $2 AND status = 'running'
|
|
20
21
|
RETURNING id)
|
|
21
22
|
SELECT id FROM changed`
|
|
22
23
|
|
|
23
24
|
/** Completes the job ($1 job id, $2 expected token, $3 instant). */
|
|
24
|
-
export const COMPLETE_JOB = sql`WITH changed AS (UPDATE jobs SET status = 'done', lease_expires_at = NULL, updated_at = $3
|
|
25
|
+
export const COMPLETE_JOB: SqlText = sql`WITH changed AS (UPDATE jobs SET status = 'done', lease_expires_at = NULL, updated_at = $3
|
|
25
26
|
WHERE id = $1 AND fencing_token = $2 AND status = 'running'
|
|
26
27
|
RETURNING id)
|
|
27
28
|
SELECT id FROM changed`
|
|
28
29
|
|
|
29
30
|
/** Fails the job ($1 job id, $2 expected token, $3 reason, $4 instant). */
|
|
30
|
-
export const FAIL_JOB = sql`WITH changed AS (UPDATE jobs SET status = 'failed', error = $3, lease_expires_at = NULL, updated_at = $4
|
|
31
|
+
export const FAIL_JOB: SqlText = sql`WITH changed AS (UPDATE jobs SET status = 'failed', error = $3, lease_expires_at = NULL, updated_at = $4
|
|
31
32
|
WHERE id = $1 AND fencing_token = $2 AND status = 'running'
|
|
32
33
|
RETURNING id)
|
|
33
34
|
SELECT id FROM changed`
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import type { DatabaseConnectionOptions } from "@modules/platform/database"
|
|
1
2
|
import { @@Name@@ProjectionEntity } from "../@@name@@.projection-entity"
|
|
2
3
|
import { Create@@Name@@Projection@@epochMs13@@ } from "./migrations/@@epochMs13@@-create-@@name@@"
|
|
3
4
|
|
|
4
5
|
/** The entities of the @@name@@ read model, for the connection that holds them. */
|
|
5
|
-
export const @@nameCamel@@Entities = [@@Name@@ProjectionEntity]
|
|
6
|
+
export const @@nameCamel@@Entities: DatabaseConnectionOptions["entities"] = [@@Name@@ProjectionEntity]
|
|
6
7
|
|
|
7
8
|
/** The migrations of the @@name@@ read model, in the order they run. */
|
|
8
|
-
export const @@nameCamel@@Migrations = [Create@@Name@@Projection@@epochMs13@@]
|
|
9
|
+
export const @@nameCamel@@Migrations: DatabaseConnectionOptions["migrations"] = [Create@@Name@@Projection@@epochMs13@@]
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { ConfigurableModuleBuilder } from "@nestjs/common"
|
|
1
|
+
import { ConfigurableModuleBuilder, type ConfigurableModuleHost } from "@nestjs/common"
|
|
2
2
|
import type { @@Name@@Options } from "./@@name@@.options"
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
export const { ConfigurableModuleClass, OPTIONS_TYPE } = new ConfigurableModuleBuilder<@@Name@@Options>()
|
|
4
|
+
const built = new ConfigurableModuleBuilder<@@Name@@Options>()
|
|
6
5
|
.setExtras({ isGlobal: false }, (definition, extras) => ({ ...definition, global: extras.isGlobal }))
|
|
7
6
|
.build()
|
|
7
|
+
type Host = ConfigurableModuleHost<@@Name@@Options, "register", "create", { isGlobal: boolean }>
|
|
8
|
+
|
|
9
|
+
/** The configurable-module base of the @@name@@ projection; `isGlobal` is decided by the app root. */
|
|
10
|
+
export const ConfigurableModuleClass: Host["ConfigurableModuleClass"] = built.ConfigurableModuleClass
|
|
11
|
+
/** The options of a synchronous registration, for the register signature. */
|
|
12
|
+
export const OPTIONS_TYPE: Host["OPTIONS_TYPE"] = built.OPTIONS_TYPE
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import type { DatabaseConnectionOptions } from "@modules/platform/database"
|
|
1
2
|
import { QueueOutboxEntity } from "./entities/queue-outbox.entity"
|
|
2
3
|
import { CreateQueueOutbox@@epochMs13@@ } from "./migrations/@@epochMs13@@-create-queue-outbox"
|
|
3
4
|
|
|
4
5
|
/** The entities of the queues, for every connection that enqueues jobs. */
|
|
5
|
-
export const queueEntities = [QueueOutboxEntity]
|
|
6
|
+
export const queueEntities: DatabaseConnectionOptions["entities"] = [QueueOutboxEntity]
|
|
6
7
|
|
|
7
8
|
/** The migrations of the queues, in the order they run. */
|
|
8
|
-
export const queueMigrations = [CreateQueueOutbox@@epochMs13@@]
|
|
9
|
+
export const queueMigrations: DatabaseConnectionOptions["migrations"] = [CreateQueueOutbox@@epochMs13@@]
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { sql } from "@modules/platform/database"
|
|
2
|
+
import type { SqlText } from "@modules/platform/database"
|
|
2
3
|
|
|
3
4
|
/** Writes one job in the outbox ($1 queue, $2 payload, $3 instant, $4 correlation id). */
|
|
4
|
-
export const INSERT_QUEUE_ROW = sql`INSERT INTO queue_outbox (queue, payload, created_at, correlation_id)
|
|
5
|
+
export const INSERT_QUEUE_ROW: SqlText = sql`INSERT INTO queue_outbox (queue, payload, created_at, correlation_id)
|
|
5
6
|
VALUES ($1, $2, $3, $4)`
|
|
6
7
|
|
|
7
8
|
/** Locks and reads the oldest rows that wait ($1 batch size); a second relay skips the rows this one holds. */
|
|
8
|
-
export const SELECT_WAITING_ROWS = sql`SELECT id, queue, payload, correlation_id AS "correlationId" FROM queue_outbox
|
|
9
|
+
export const SELECT_WAITING_ROWS: SqlText = sql`SELECT id, queue, payload, correlation_id AS "correlationId" FROM queue_outbox
|
|
9
10
|
WHERE sent_at IS NULL ORDER BY created_at, id LIMIT $1 FOR UPDATE SKIP LOCKED`
|
|
10
11
|
|
|
11
12
|
/** Marks the rows as handed to BullMQ ($1 ids, $2 instant). */
|
|
12
|
-
export const MARK_ROWS_SENT = sql`UPDATE queue_outbox SET sent_at = $2 WHERE id = ANY($1::uuid[])`
|
|
13
|
+
export const MARK_ROWS_SENT: SqlText = sql`UPDATE queue_outbox SET sent_at = $2 WHERE id = ANY($1::uuid[])`
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import { ConfigurableModuleBuilder } from "@nestjs/common"
|
|
1
|
+
import { ConfigurableModuleBuilder, type ConfigurableModuleHost } from "@nestjs/common"
|
|
2
2
|
import { QUEUE_OPTIONS } from "./queue.decorators"
|
|
3
3
|
import type { QueueRegistrationOptions } from "./queue.options"
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
export const { ConfigurableModuleClass, OPTIONS_TYPE } = new ConfigurableModuleBuilder<QueueRegistrationOptions>({
|
|
5
|
+
const built = new ConfigurableModuleBuilder<QueueRegistrationOptions>({
|
|
7
6
|
optionsInjectionToken: QUEUE_OPTIONS,
|
|
8
7
|
})
|
|
9
8
|
.setExtras({ isGlobal: false }, (definition, extras) => ({ ...definition, global: extras.isGlobal }))
|
|
10
9
|
.build()
|
|
10
|
+
type Host = ConfigurableModuleHost<QueueRegistrationOptions, "register", "create", { isGlobal: boolean }>
|
|
11
|
+
|
|
12
|
+
/** The configurable-module base of the queues; `isGlobal` is decided by the app root. */
|
|
13
|
+
export const ConfigurableModuleClass: Host["ConfigurableModuleClass"] = built.ConfigurableModuleClass
|
|
14
|
+
/** The options of a synchronous registration, for the register signature. */
|
|
15
|
+
export const OPTIONS_TYPE: Host["OPTIONS_TYPE"] = built.OPTIONS_TYPE
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import { ConfigurableModuleBuilder } from "@nestjs/common"
|
|
1
|
+
import { ConfigurableModuleBuilder, type ConfigurableModuleHost } from "@nestjs/common"
|
|
2
2
|
import type { @@Queue@@Options } from "./@@queue@@.options"
|
|
3
3
|
|
|
4
|
+
const built = new ConfigurableModuleBuilder<@@Queue@@Options>().setClassMethodName("register").build()
|
|
5
|
+
type Host = ConfigurableModuleHost<@@Queue@@Options, "register", "create", {}>
|
|
6
|
+
|
|
4
7
|
/** The configurable-module base of the @@queue@@ module. */
|
|
5
|
-
export const
|
|
6
|
-
|
|
8
|
+
export const ConfigurableModuleClass: Host["ConfigurableModuleClass"] = built.ConfigurableModuleClass
|
|
9
|
+
/** The injection token of the registered options. */
|
|
10
|
+
export const MODULE_OPTIONS_TOKEN: Host["MODULE_OPTIONS_TOKEN"] = built.MODULE_OPTIONS_TOKEN
|
|
11
|
+
/** The options of a synchronous registration, for the register signature. */
|
|
12
|
+
export const OPTIONS_TYPE: Host["OPTIONS_TYPE"] = built.OPTIONS_TYPE
|
|
@@ -8,7 +8,7 @@ import { @@Action@@Command } from "../../application/@@action@@.command"
|
|
|
8
8
|
@Injectable()
|
|
9
9
|
/** The consumer door of the @@event@@ event in the @@reactor@@ reactor: it dispatches one command and forwards the event id. */
|
|
10
10
|
export class @@Event@@Consumer implements EventConsumer<@@Event@@Event> {
|
|
11
|
-
readonly event = @@Event@@Event
|
|
11
|
+
readonly event: typeof @@Event@@Event = @@Event@@Event
|
|
12
12
|
|
|
13
13
|
constructor(@InjectCommandBus() private readonly commandBus: CommandBus) {}
|
|
14
14
|
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import { ConfigurableModuleBuilder } from "@nestjs/common"
|
|
1
|
+
import { ConfigurableModuleBuilder, type ConfigurableModuleHost } from "@nestjs/common"
|
|
2
2
|
import type { @@Reactor@@MessageOptions } from "./@@reactor@@-message.options"
|
|
3
3
|
|
|
4
|
+
const built = new ConfigurableModuleBuilder<@@Reactor@@MessageOptions>().setClassMethodName("register").build()
|
|
5
|
+
type Host = ConfigurableModuleHost<@@Reactor@@MessageOptions, "register", "create", {}>
|
|
6
|
+
|
|
4
7
|
/** The configurable-module base of the @@reactor@@-message module. */
|
|
5
|
-
export const
|
|
6
|
-
|
|
8
|
+
export const ConfigurableModuleClass: Host["ConfigurableModuleClass"] = built.ConfigurableModuleClass
|
|
9
|
+
/** The injection token of the registered options. */
|
|
10
|
+
export const MODULE_OPTIONS_TOKEN: Host["MODULE_OPTIONS_TOKEN"] = built.MODULE_OPTIONS_TOKEN
|
|
11
|
+
/** The options of a synchronous registration, for the register signature. */
|
|
12
|
+
export const OPTIONS_TYPE: Host["OPTIONS_TYPE"] = built.OPTIONS_TYPE
|