@pattern-stack/codegen 0.11.0 → 0.12.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 +60 -0
- package/dist/runtime/subsystems/index.d.ts +7 -3
- package/dist/runtime/subsystems/index.js +993 -19
- package/dist/runtime/subsystems/index.js.map +1 -1
- package/dist/runtime/subsystems/integration/entity-change-source-registry.memory.d.ts +25 -0
- package/dist/runtime/subsystems/integration/entity-change-source-registry.memory.js +34 -0
- package/dist/runtime/subsystems/integration/entity-change-source-registry.memory.js.map +1 -0
- package/dist/runtime/subsystems/integration/entity-change-source-registry.protocol.d.ts +53 -0
- package/dist/runtime/subsystems/integration/entity-change-source-registry.protocol.js +13 -0
- package/dist/runtime/subsystems/integration/entity-change-source-registry.protocol.js.map +1 -0
- package/dist/runtime/subsystems/integration/execute-integration.use-case.js.map +1 -1
- package/dist/runtime/subsystems/integration/index.d.ts +3 -1
- package/dist/runtime/subsystems/integration/index.js +35 -0
- package/dist/runtime/subsystems/integration/index.js.map +1 -1
- package/dist/runtime/subsystems/integration/integration-cursor-store.drizzle-backend.js.map +1 -1
- package/dist/runtime/subsystems/integration/integration-run-recorder.drizzle-backend.js.map +1 -1
- package/dist/runtime/subsystems/integration/integration.module.js.map +1 -1
- package/dist/runtime/subsystems/integration/integration.tokens.d.ts +14 -1
- package/dist/runtime/subsystems/integration/integration.tokens.js +2 -0
- package/dist/runtime/subsystems/integration/integration.tokens.js.map +1 -1
- package/dist/runtime/subsystems/observability/index.js.map +1 -1
- package/dist/runtime/subsystems/observability/observability.module.js.map +1 -1
- package/dist/runtime/subsystems/observability/observability.service.js.map +1 -1
- package/dist/src/cli/index.js +1074 -107
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/index.d.ts +48 -0
- package/dist/src/index.js +99 -3
- package/dist/src/index.js.map +1 -1
- package/package.json +9 -1
- package/runtime/subsystems/index.ts +15 -0
- package/runtime/subsystems/integration/entity-change-source-registry.memory.ts +40 -0
- package/runtime/subsystems/integration/entity-change-source-registry.protocol.ts +59 -0
- package/runtime/subsystems/integration/index.ts +9 -0
- package/runtime/subsystems/integration/integration.tokens.ts +14 -0
- package/templates/entity/new/clean-lite-ps/entity.ejs.t +12 -3
- package/templates/entity/new/clean-lite-ps/prompt-extension.js +212 -29
- package/templates/entity/new/backend/modules/core/integration-source.providers.ejs.t +0 -18
package/dist/src/cli/index.js
CHANGED
|
@@ -12,8 +12,8 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
12
12
|
var __decorateParam = (index2, decorator) => (target, key) => decorator(target, key, index2);
|
|
13
13
|
|
|
14
14
|
// src/cli/index.ts
|
|
15
|
-
import { readFileSync as
|
|
16
|
-
import { join as
|
|
15
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
16
|
+
import { join as join13 } from "path";
|
|
17
17
|
import { Builtins, Cli, Command as Command13 } from "clipanion";
|
|
18
18
|
|
|
19
19
|
// src/cli/noun-module.ts
|
|
@@ -2465,6 +2465,36 @@ var EntityDefinitionSchema = z3.object({
|
|
|
2465
2465
|
// appear in `integration.providers` — see the superRefine on
|
|
2466
2466
|
// `EntityDefinitionSchema` below.
|
|
2467
2467
|
detection: z3.record(z3.string(), DetectionConfigSchema).optional(),
|
|
2468
|
+
// RFC-0001 §1/§8: the integration *surface* this entity belongs to
|
|
2469
|
+
// (e.g. 'calendar', 'mail', 'crm'). Surfaces span provider contexts
|
|
2470
|
+
// (ADR-0006) — one Google OAuth feeds calendar+mail+transcript. The union
|
|
2471
|
+
// of `surface:` values across all entity YAML is the closed set that a
|
|
2472
|
+
// provider's `surfaces:` must be a subset of (cross-checked in
|
|
2473
|
+
// src/parser/validate-providers.ts). Optional: entities without an
|
|
2474
|
+
// integration surface omit it. The surface-package *emission* convention
|
|
2475
|
+
// is Track C (#329); this field is only the declarative input both tracks
|
|
2476
|
+
// read.
|
|
2477
|
+
surface: z3.string().optional(),
|
|
2478
|
+
// Bounded-context declaration (ADR-0004) — "which bounded context this
|
|
2479
|
+
// entity belongs to". This is the DURABLE decision; it is a plain
|
|
2480
|
+
// bounded-context slug, NOT a folder knob. Different features consume it:
|
|
2481
|
+
//
|
|
2482
|
+
// - #403 (this PR, the FIRST consumer): drives the generated code's
|
|
2483
|
+
// module output folder. clean-lite-ps nests the entity's module under
|
|
2484
|
+
// `<modules>/<context>/<entity>/` so same-context entities group
|
|
2485
|
+
// together; untagged entities stay flat (`<modules>/<entity>/`).
|
|
2486
|
+
// - ADR-0004 (deferred): a later `naming: prefix | schema` knob reads
|
|
2487
|
+
// this SAME field to drive the Postgres physical layout —
|
|
2488
|
+
// `prefix` → `pgTable('<context>__<table>')`, then the flip to
|
|
2489
|
+
// `schema` → `pgSchema('<context>').table('<table>')`. NOT wired here;
|
|
2490
|
+
// #403 makes no table/column/schema changes.
|
|
2491
|
+
//
|
|
2492
|
+
// Sibling to `surface:` and orthogonal to it (ADR-0006): context = model
|
|
2493
|
+
// cohesion (which domain), surface = vendor composition (which integration).
|
|
2494
|
+
context: z3.string().regex(
|
|
2495
|
+
/^[a-z][a-z0-9_]*$/,
|
|
2496
|
+
"context must be lowercase snake_case (e.g. 'integration')"
|
|
2497
|
+
).optional(),
|
|
2468
2498
|
// v2: Domain event declarations (CODEGEN-EVOLUTION-PLAN Phase 2)
|
|
2469
2499
|
// Generates typed event classes, handlers, and queue registration
|
|
2470
2500
|
events: z3.array(EventDeclarationSchema).optional(),
|
|
@@ -2483,7 +2513,18 @@ var EntityDefinitionSchema = z3.object({
|
|
|
2483
2513
|
).optional(),
|
|
2484
2514
|
// v2: Analytics / semantic layer configuration
|
|
2485
2515
|
// Cube.js measure packs, custom cube name, and metric definitions
|
|
2486
|
-
analytics: AnalyticsBlockSchema.optional()
|
|
2516
|
+
analytics: AnalyticsBlockSchema.optional(),
|
|
2517
|
+
// Composite (multi-column) unique indexes (#356). Single-column uniqueness
|
|
2518
|
+
// is `unique: true` on the field itself; this declares constraints that
|
|
2519
|
+
// span 2+ columns, e.g. UNIQUE (conversation_id, sequence). Emitted as a
|
|
2520
|
+
// `uniqueIndex(...).on(...)` entry in the generated entity's pgTable
|
|
2521
|
+
// extra-config callback. `name` defaults to <table>_<col1>_<col2>_uniq.
|
|
2522
|
+
unique_indexes: z3.array(
|
|
2523
|
+
z3.object({
|
|
2524
|
+
fields: z3.array(z3.string()).min(2, "unique_indexes entries span 2+ columns \u2014 use `unique: true` on the field for single-column uniqueness"),
|
|
2525
|
+
name: z3.string().optional()
|
|
2526
|
+
}).strict()
|
|
2527
|
+
).optional()
|
|
2487
2528
|
}).strict().refine(
|
|
2488
2529
|
(data) => !data.eav_value_table || typeof data.eav_definition_table === "string",
|
|
2489
2530
|
{
|
|
@@ -2910,6 +2951,60 @@ var JunctionDefinitionSchema = z6.object({
|
|
|
2910
2951
|
}
|
|
2911
2952
|
);
|
|
2912
2953
|
|
|
2954
|
+
// src/schema/provider-definition.schema.ts
|
|
2955
|
+
import { z as z7 } from "zod";
|
|
2956
|
+
var IMPORT_REF_RE = /^[^#\s]+#[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
2957
|
+
var ImportRefSchema = z7.string().regex(
|
|
2958
|
+
IMPORT_REF_RE,
|
|
2959
|
+
"must be an 'import-path#Export' reference (e.g. '@app/foo/bar.strategy#BarStrategy')"
|
|
2960
|
+
);
|
|
2961
|
+
function parseImportRef(ref) {
|
|
2962
|
+
const hash = ref.indexOf("#");
|
|
2963
|
+
return { path: ref.slice(0, hash), exportName: ref.slice(hash + 1) };
|
|
2964
|
+
}
|
|
2965
|
+
var AuthTypeSchema = z7.enum(["oauth2", "api-key", "app-password"]);
|
|
2966
|
+
var AuthSchema = z7.object({
|
|
2967
|
+
type: AuthTypeSchema,
|
|
2968
|
+
// Class implementing the auth subsystem's strategy contract (ADR-031).
|
|
2969
|
+
// Pre-flight verified against a real export at codegen time.
|
|
2970
|
+
strategy: ImportRefSchema,
|
|
2971
|
+
// Required (and non-empty) iff type === 'oauth2'; see refine below.
|
|
2972
|
+
scopes: z7.array(z7.string()).optional()
|
|
2973
|
+
}).strict().refine(
|
|
2974
|
+
(a) => a.type !== "oauth2" || a.scopes !== void 0 && a.scopes.length > 0,
|
|
2975
|
+
{
|
|
2976
|
+
message: "auth.scopes is required and must be non-empty when auth.type is 'oauth2'",
|
|
2977
|
+
path: ["scopes"]
|
|
2978
|
+
}
|
|
2979
|
+
);
|
|
2980
|
+
var ClientSchema = z7.object({
|
|
2981
|
+
// API client class. Pre-flight verified against a real export.
|
|
2982
|
+
class: ImportRefSchema,
|
|
2983
|
+
base_url: z7.string().url("client.base_url must be an absolute URL")
|
|
2984
|
+
}).strict();
|
|
2985
|
+
var ProviderDefinitionSchema = z7.object({
|
|
2986
|
+
// Provider id — the canonical string used as detection: keys, audit rows,
|
|
2987
|
+
// subscription rows. kebab/lower; unique across definitions/providers/
|
|
2988
|
+
// (uniqueness is a cross-file check in validate-providers.ts).
|
|
2989
|
+
slug: z7.string().regex(
|
|
2990
|
+
/^[a-z][a-z0-9-]*$/,
|
|
2991
|
+
"slug must be kebab-case lower (e.g. 'google', 'hubspot')"
|
|
2992
|
+
),
|
|
2993
|
+
display_name: z7.string().optional(),
|
|
2994
|
+
auth: AuthSchema,
|
|
2995
|
+
client: ClientSchema,
|
|
2996
|
+
// Surfaces this provider serves (ADR-0006: surfaces span contexts — one
|
|
2997
|
+
// Google OAuth feeds calendar+mail+transcript). Each must reference a real
|
|
2998
|
+
// `surface:` declared on some entity; that cross-check is in
|
|
2999
|
+
// validate-providers.ts. Non-empty enforced here.
|
|
3000
|
+
surfaces: z7.array(z7.string()).min(1, "surfaces must list at least one surface"),
|
|
3001
|
+
// Optional auth lifecycle hints consumed by provider-module emission (D2).
|
|
3002
|
+
// `refresh_behavior` is left as a free string in D1 — its domain firms up
|
|
3003
|
+
// when D2 consumes it; carrying it now keeps the YAML lossless.
|
|
3004
|
+
token_lifetime: z7.number().int().positive().optional(),
|
|
3005
|
+
refresh_behavior: z7.string().optional()
|
|
3006
|
+
}).strict();
|
|
3007
|
+
|
|
2913
3008
|
// src/utils/yaml-loader.ts
|
|
2914
3009
|
function loadEntityFromYaml(filePath) {
|
|
2915
3010
|
if (!existsSync6(filePath)) {
|
|
@@ -2963,6 +3058,19 @@ function formatZodErrors(error) {
|
|
|
2963
3058
|
return `${err.message} ${location}`;
|
|
2964
3059
|
});
|
|
2965
3060
|
}
|
|
3061
|
+
function loadEntitiesFromYaml(filePaths) {
|
|
3062
|
+
const successes = [];
|
|
3063
|
+
const failures = [];
|
|
3064
|
+
for (const filePath of filePaths) {
|
|
3065
|
+
const result = loadEntityFromYaml(filePath);
|
|
3066
|
+
if (result.success) {
|
|
3067
|
+
successes.push(result);
|
|
3068
|
+
} else {
|
|
3069
|
+
failures.push(result);
|
|
3070
|
+
}
|
|
3071
|
+
}
|
|
3072
|
+
return { successes, failures };
|
|
3073
|
+
}
|
|
2966
3074
|
function loadRelationshipFromYaml(filePath) {
|
|
2967
3075
|
if (!existsSync6(filePath)) {
|
|
2968
3076
|
return {
|
|
@@ -3098,6 +3206,64 @@ function loadJunctionFromYaml(filePath) {
|
|
|
3098
3206
|
filePath
|
|
3099
3207
|
};
|
|
3100
3208
|
}
|
|
3209
|
+
function loadProviderFromYaml(filePath) {
|
|
3210
|
+
if (!existsSync6(filePath)) {
|
|
3211
|
+
return {
|
|
3212
|
+
success: false,
|
|
3213
|
+
error: `File not found: ${filePath}`,
|
|
3214
|
+
filePath
|
|
3215
|
+
};
|
|
3216
|
+
}
|
|
3217
|
+
let content;
|
|
3218
|
+
try {
|
|
3219
|
+
content = readFileSync4(filePath, "utf-8");
|
|
3220
|
+
} catch (err) {
|
|
3221
|
+
return {
|
|
3222
|
+
success: false,
|
|
3223
|
+
error: `Failed to read file: ${filePath}`,
|
|
3224
|
+
details: [err instanceof Error ? err.message : String(err)],
|
|
3225
|
+
filePath
|
|
3226
|
+
};
|
|
3227
|
+
}
|
|
3228
|
+
let parsed;
|
|
3229
|
+
try {
|
|
3230
|
+
parsed = parseYaml(content);
|
|
3231
|
+
} catch (err) {
|
|
3232
|
+
return {
|
|
3233
|
+
success: false,
|
|
3234
|
+
error: `Invalid YAML syntax in ${filePath}`,
|
|
3235
|
+
details: [err instanceof Error ? err.message : String(err)],
|
|
3236
|
+
filePath
|
|
3237
|
+
};
|
|
3238
|
+
}
|
|
3239
|
+
const result = ProviderDefinitionSchema.safeParse(parsed);
|
|
3240
|
+
if (!result.success) {
|
|
3241
|
+
return {
|
|
3242
|
+
success: false,
|
|
3243
|
+
error: `Validation failed for ${filePath}`,
|
|
3244
|
+
details: formatZodErrors(result.error),
|
|
3245
|
+
filePath
|
|
3246
|
+
};
|
|
3247
|
+
}
|
|
3248
|
+
return {
|
|
3249
|
+
success: true,
|
|
3250
|
+
definition: result.data,
|
|
3251
|
+
filePath
|
|
3252
|
+
};
|
|
3253
|
+
}
|
|
3254
|
+
function loadProvidersFromYaml(filePaths) {
|
|
3255
|
+
const successes = [];
|
|
3256
|
+
const failures = [];
|
|
3257
|
+
for (const filePath of filePaths) {
|
|
3258
|
+
const result = loadProviderFromYaml(filePath);
|
|
3259
|
+
if (result.success) {
|
|
3260
|
+
successes.push(result);
|
|
3261
|
+
} else {
|
|
3262
|
+
failures.push(result);
|
|
3263
|
+
}
|
|
3264
|
+
}
|
|
3265
|
+
return { successes, failures };
|
|
3266
|
+
}
|
|
3101
3267
|
function detectYamlType(filePath) {
|
|
3102
3268
|
if (!existsSync6(filePath)) return "unknown";
|
|
3103
3269
|
try {
|
|
@@ -3477,6 +3643,160 @@ function resolveRelationshipReferences(relationshipDefs, entities) {
|
|
|
3477
3643
|
return issues;
|
|
3478
3644
|
}
|
|
3479
3645
|
|
|
3646
|
+
// src/parser/validate-providers.ts
|
|
3647
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
|
|
3648
|
+
import { isAbsolute, join as join8, resolve as resolve3 } from "path";
|
|
3649
|
+
import ts from "typescript";
|
|
3650
|
+
function collectEntitySurfaces(entities) {
|
|
3651
|
+
const surfaces = /* @__PURE__ */ new Set();
|
|
3652
|
+
for (const e of entities) {
|
|
3653
|
+
if (e.surface) surfaces.add(e.surface);
|
|
3654
|
+
}
|
|
3655
|
+
return surfaces;
|
|
3656
|
+
}
|
|
3657
|
+
function resolveImportRef(ref, opts) {
|
|
3658
|
+
const { path: path34, exportName } = parseImportRef(ref);
|
|
3659
|
+
const file = resolveModuleFile(path34, opts);
|
|
3660
|
+
if (!file) {
|
|
3661
|
+
return { status: "module-not-found", resolvedFrom: opts.sourceRoot };
|
|
3662
|
+
}
|
|
3663
|
+
const content = readFileSync5(file, "utf-8");
|
|
3664
|
+
const { names: names2, hasWildcard } = collectExportedNames(file, content);
|
|
3665
|
+
if (names2.has(exportName) || hasWildcard) {
|
|
3666
|
+
return { status: "ok", file };
|
|
3667
|
+
}
|
|
3668
|
+
return { status: "export-not-found", file };
|
|
3669
|
+
}
|
|
3670
|
+
function resolveModuleFile(importPath, opts) {
|
|
3671
|
+
let base = null;
|
|
3672
|
+
for (const [alias, target] of Object.entries(opts.aliases ?? {})) {
|
|
3673
|
+
if (importPath === alias || importPath.startsWith(`${alias}/`)) {
|
|
3674
|
+
const rest = importPath.slice(alias.length);
|
|
3675
|
+
base = join8(target, rest);
|
|
3676
|
+
break;
|
|
3677
|
+
}
|
|
3678
|
+
}
|
|
3679
|
+
if (base === null) {
|
|
3680
|
+
base = isAbsolute(importPath) ? importPath : resolve3(opts.sourceRoot, importPath);
|
|
3681
|
+
}
|
|
3682
|
+
const candidates = [
|
|
3683
|
+
base,
|
|
3684
|
+
`${base}.ts`,
|
|
3685
|
+
`${base}.tsx`,
|
|
3686
|
+
join8(base, "index.ts"),
|
|
3687
|
+
join8(base, "index.tsx")
|
|
3688
|
+
];
|
|
3689
|
+
for (const c of candidates) {
|
|
3690
|
+
if (existsSync7(c) && statSync3(c).isFile()) return c;
|
|
3691
|
+
}
|
|
3692
|
+
return null;
|
|
3693
|
+
}
|
|
3694
|
+
function collectExportedNames(fileName, content) {
|
|
3695
|
+
const sf = ts.createSourceFile(
|
|
3696
|
+
fileName,
|
|
3697
|
+
content,
|
|
3698
|
+
ts.ScriptTarget.Latest,
|
|
3699
|
+
/* setParentNodes */
|
|
3700
|
+
true
|
|
3701
|
+
);
|
|
3702
|
+
const names2 = /* @__PURE__ */ new Set();
|
|
3703
|
+
let hasWildcard = false;
|
|
3704
|
+
const hasExportModifier = (node) => ts.canHaveModifiers(node) && (ts.getModifiers(node)?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false);
|
|
3705
|
+
sf.forEachChild((node) => {
|
|
3706
|
+
if (hasExportModifier(node)) {
|
|
3707
|
+
if ((ts.isClassDeclaration(node) || ts.isFunctionDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node) || ts.isEnumDeclaration(node) || ts.isModuleDeclaration(node)) && node.name && ts.isIdentifier(node.name)) {
|
|
3708
|
+
names2.add(node.name.text);
|
|
3709
|
+
} else if (ts.isVariableStatement(node)) {
|
|
3710
|
+
for (const decl of node.declarationList.declarations) {
|
|
3711
|
+
if (ts.isIdentifier(decl.name)) names2.add(decl.name.text);
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
if (ts.isExportDeclaration(node)) {
|
|
3716
|
+
if (!node.exportClause) {
|
|
3717
|
+
hasWildcard = true;
|
|
3718
|
+
} else if (ts.isNamedExports(node.exportClause)) {
|
|
3719
|
+
for (const el of node.exportClause.elements) names2.add(el.name.text);
|
|
3720
|
+
} else if (ts.isNamespaceExport(node.exportClause)) {
|
|
3721
|
+
names2.add(node.exportClause.name.text);
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
});
|
|
3725
|
+
return { names: names2, hasWildcard };
|
|
3726
|
+
}
|
|
3727
|
+
function validateProviders(providers, opts) {
|
|
3728
|
+
const issues = [];
|
|
3729
|
+
const knownSurfaces = new Set(opts.entitySurfaces);
|
|
3730
|
+
const slugToFiles = /* @__PURE__ */ new Map();
|
|
3731
|
+
for (const p of providers) {
|
|
3732
|
+
const files = slugToFiles.get(p.definition.slug) ?? [];
|
|
3733
|
+
files.push(p.filePath);
|
|
3734
|
+
slugToFiles.set(p.definition.slug, files);
|
|
3735
|
+
}
|
|
3736
|
+
for (const [slug, files] of slugToFiles) {
|
|
3737
|
+
if (files.length > 1) {
|
|
3738
|
+
for (const file of files) {
|
|
3739
|
+
const others = files.filter((f) => f !== file);
|
|
3740
|
+
issues.push({
|
|
3741
|
+
severity: "error",
|
|
3742
|
+
type: "provider_duplicate_slug",
|
|
3743
|
+
message: `provider slug '${slug}' is declared more than once (also in: ${others.join(", ")})`,
|
|
3744
|
+
path: file
|
|
3745
|
+
});
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
for (const { definition, filePath } of providers) {
|
|
3750
|
+
const { slug } = definition;
|
|
3751
|
+
for (const surface of definition.surfaces) {
|
|
3752
|
+
if (!knownSurfaces.has(surface)) {
|
|
3753
|
+
const known = [...knownSurfaces].sort().join(", ") || "(none declared)";
|
|
3754
|
+
issues.push({
|
|
3755
|
+
severity: "error",
|
|
3756
|
+
type: "provider_unknown_surface",
|
|
3757
|
+
message: `provider ${slug}: surface '${surface}' is not declared by any entity (known surfaces: ${known})`,
|
|
3758
|
+
path: filePath
|
|
3759
|
+
});
|
|
3760
|
+
}
|
|
3761
|
+
}
|
|
3762
|
+
if (!opts.skipImportCheck) {
|
|
3763
|
+
if (!opts.sourceRoot) {
|
|
3764
|
+
throw new Error(
|
|
3765
|
+
"validateProviders: sourceRoot is required for the import pre-flight check (or set skipImportCheck: true)"
|
|
3766
|
+
);
|
|
3767
|
+
}
|
|
3768
|
+
const resolveOpts = {
|
|
3769
|
+
sourceRoot: opts.sourceRoot,
|
|
3770
|
+
aliases: opts.aliases
|
|
3771
|
+
};
|
|
3772
|
+
const refs = [
|
|
3773
|
+
{ field: "auth.strategy", ref: definition.auth.strategy },
|
|
3774
|
+
{ field: "client.class", ref: definition.client.class }
|
|
3775
|
+
];
|
|
3776
|
+
for (const { field, ref } of refs) {
|
|
3777
|
+
const result = resolveImportRef(ref, resolveOpts);
|
|
3778
|
+
if (result.status === "module-not-found") {
|
|
3779
|
+
issues.push({
|
|
3780
|
+
severity: "error",
|
|
3781
|
+
type: "provider_import_unresolved",
|
|
3782
|
+
message: `provider ${slug}: ${field} '${ref}' not found \u2014 module could not be resolved from ${result.resolvedFrom}`,
|
|
3783
|
+
path: filePath
|
|
3784
|
+
});
|
|
3785
|
+
} else if (result.status === "export-not-found") {
|
|
3786
|
+
const { exportName } = parseImportRef(ref);
|
|
3787
|
+
issues.push({
|
|
3788
|
+
severity: "error",
|
|
3789
|
+
type: "provider_import_unresolved",
|
|
3790
|
+
message: `provider ${slug}: ${field} '${ref}' not found \u2014 export '${exportName}' is missing from ${result.file}`,
|
|
3791
|
+
path: filePath
|
|
3792
|
+
});
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3796
|
+
}
|
|
3797
|
+
return issues;
|
|
3798
|
+
}
|
|
3799
|
+
|
|
3480
3800
|
// src/analyzer/graph-builder.ts
|
|
3481
3801
|
function inferCardinality(type) {
|
|
3482
3802
|
switch (type) {
|
|
@@ -4290,28 +4610,28 @@ function createSuggestion(path34) {
|
|
|
4290
4610
|
|
|
4291
4611
|
// src/analyzer/manifest.ts
|
|
4292
4612
|
import { createHash } from "crypto";
|
|
4293
|
-
import { readFileSync as
|
|
4294
|
-
import { join as
|
|
4613
|
+
import { readFileSync as readFileSync6, writeFileSync, existsSync as existsSync8, mkdirSync, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
|
|
4614
|
+
import { join as join9 } from "path";
|
|
4295
4615
|
var MANIFEST_FILE = "manifest.json";
|
|
4296
4616
|
var MANIFEST_VERSION = 1;
|
|
4297
4617
|
function getManifestDir() {
|
|
4298
4618
|
return process.env.CODEGEN_MANIFEST_DIR || ".codegen";
|
|
4299
4619
|
}
|
|
4300
4620
|
function getManifestPaths(projectRoot) {
|
|
4301
|
-
const dir =
|
|
4302
|
-
const file =
|
|
4621
|
+
const dir = join9(projectRoot, getManifestDir());
|
|
4622
|
+
const file = join9(dir, MANIFEST_FILE);
|
|
4303
4623
|
return { dir, file };
|
|
4304
4624
|
}
|
|
4305
4625
|
async function computeEntityFilesHash(entitiesDir) {
|
|
4306
|
-
if (!
|
|
4626
|
+
if (!existsSync8(entitiesDir)) {
|
|
4307
4627
|
return createHash("sha256").update("").digest("hex");
|
|
4308
4628
|
}
|
|
4309
4629
|
const yamlFiles = [];
|
|
4310
4630
|
function walkDir(dir) {
|
|
4311
4631
|
const entries = readdirSync6(dir);
|
|
4312
4632
|
for (const entry of entries) {
|
|
4313
|
-
const fullPath =
|
|
4314
|
-
const stat =
|
|
4633
|
+
const fullPath = join9(dir, entry);
|
|
4634
|
+
const stat = statSync4(fullPath);
|
|
4315
4635
|
if (stat.isDirectory()) {
|
|
4316
4636
|
walkDir(fullPath);
|
|
4317
4637
|
} else if (stat.isFile() && (entry.endsWith(".yaml") || entry.endsWith(".yml"))) {
|
|
@@ -4323,7 +4643,7 @@ async function computeEntityFilesHash(entitiesDir) {
|
|
|
4323
4643
|
yamlFiles.sort();
|
|
4324
4644
|
const hash = createHash("sha256");
|
|
4325
4645
|
for (const file of yamlFiles) {
|
|
4326
|
-
const content =
|
|
4646
|
+
const content = readFileSync6(file, "utf-8");
|
|
4327
4647
|
hash.update(file);
|
|
4328
4648
|
hash.update(content);
|
|
4329
4649
|
}
|
|
@@ -4331,11 +4651,11 @@ async function computeEntityFilesHash(entitiesDir) {
|
|
|
4331
4651
|
}
|
|
4332
4652
|
function readManifest(projectRoot) {
|
|
4333
4653
|
const { file } = getManifestPaths(projectRoot);
|
|
4334
|
-
if (!
|
|
4654
|
+
if (!existsSync8(file)) {
|
|
4335
4655
|
return null;
|
|
4336
4656
|
}
|
|
4337
4657
|
try {
|
|
4338
|
-
const content =
|
|
4658
|
+
const content = readFileSync6(file, "utf-8");
|
|
4339
4659
|
const manifest = JSON.parse(content);
|
|
4340
4660
|
if (manifest.version !== MANIFEST_VERSION) {
|
|
4341
4661
|
return null;
|
|
@@ -4347,7 +4667,7 @@ function readManifest(projectRoot) {
|
|
|
4347
4667
|
}
|
|
4348
4668
|
function writeManifest(projectRoot, manifest) {
|
|
4349
4669
|
const { dir, file } = getManifestPaths(projectRoot);
|
|
4350
|
-
if (!
|
|
4670
|
+
if (!existsSync8(dir)) {
|
|
4351
4671
|
mkdirSync(dir, { recursive: true });
|
|
4352
4672
|
}
|
|
4353
4673
|
const content = JSON.stringify(manifest, null, 2);
|
|
@@ -5442,8 +5762,8 @@ var BasePattern = definePattern({
|
|
|
5442
5762
|
});
|
|
5443
5763
|
|
|
5444
5764
|
// src/patterns/library/junction.pattern.ts
|
|
5445
|
-
import { z as
|
|
5446
|
-
var JunctionPatternConfigSchema =
|
|
5765
|
+
import { z as z8 } from "zod";
|
|
5766
|
+
var JunctionPatternConfigSchema = z8.object({}).strict();
|
|
5447
5767
|
var JunctionPattern = definePattern({
|
|
5448
5768
|
name: "Junction",
|
|
5449
5769
|
description: "Explicit many-to-many junction with role + temporal + sourcing metadata",
|
|
@@ -5572,9 +5892,9 @@ function validateEntities(entitiesDir) {
|
|
|
5572
5892
|
|
|
5573
5893
|
// src/cli/shared/hygen.ts
|
|
5574
5894
|
import { execSync } from "child_process";
|
|
5575
|
-
import { join as
|
|
5895
|
+
import { join as join10 } from "path";
|
|
5576
5896
|
function defaultTemplateRoot() {
|
|
5577
|
-
return
|
|
5897
|
+
return join10(import.meta.dirname, "..", "..", "..", "templates");
|
|
5578
5898
|
}
|
|
5579
5899
|
function quoteArg(a) {
|
|
5580
5900
|
if (a === "" || /[\s"'$`\\]/.test(a)) {
|
|
@@ -5698,7 +6018,9 @@ function collectEntities(entitiesDir) {
|
|
|
5698
6018
|
const def = result.definition;
|
|
5699
6019
|
entities.push({
|
|
5700
6020
|
name: def.entity.name,
|
|
5701
|
-
plural: def.entity.plural
|
|
6021
|
+
plural: def.entity.plural,
|
|
6022
|
+
// #403: top-level `context:` nests the module folder; undefined → flat.
|
|
6023
|
+
context: def.context
|
|
5702
6024
|
});
|
|
5703
6025
|
}
|
|
5704
6026
|
entities.sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -5754,11 +6076,12 @@ function entityFilePaths(info, architecture, backendSrc) {
|
|
|
5754
6076
|
const pluralKebab = toKebabCase(plural);
|
|
5755
6077
|
if (architecture === "clean-lite-ps") {
|
|
5756
6078
|
const prefix = backendSrc && backendSrc !== "." ? `${backendSrc}/` : "";
|
|
6079
|
+
const ctxSeg = info.context ? `${info.context}/` : "";
|
|
5757
6080
|
return {
|
|
5758
|
-
moduleFile: `${prefix}modules/${plural}/${plural}.module.ts`,
|
|
6081
|
+
moduleFile: `${prefix}modules/${ctxSeg}${plural}/${plural}.module.ts`,
|
|
5759
6082
|
moduleClass: `${toPascalCase(plural)}Module`,
|
|
5760
6083
|
// Drizzle entity schema lives alongside the entity file in clean-lite-ps.
|
|
5761
|
-
schemaFile: `${prefix}modules/${plural}/${name}.entity.ts`
|
|
6084
|
+
schemaFile: `${prefix}modules/${ctxSeg}${plural}/${name}.entity.ts`
|
|
5762
6085
|
};
|
|
5763
6086
|
}
|
|
5764
6087
|
return {
|
|
@@ -5879,22 +6202,22 @@ var HEADER2 = `// AUTO-GENERATED by @pattern-stack/codegen. Do not edit.
|
|
|
5879
6202
|
function collectScopeableNames(entitiesDir) {
|
|
5880
6203
|
if (!fs3.existsSync(entitiesDir)) return [];
|
|
5881
6204
|
const files = findYamlFiles(entitiesDir);
|
|
5882
|
-
const
|
|
6205
|
+
const names2 = [];
|
|
5883
6206
|
for (const file of files) {
|
|
5884
6207
|
const result = loadEntityFromYaml(file);
|
|
5885
6208
|
if (!result.success) continue;
|
|
5886
6209
|
if (result.definition.entity.scopeable === true) {
|
|
5887
|
-
|
|
6210
|
+
names2.push(result.definition.entity.name);
|
|
5888
6211
|
}
|
|
5889
6212
|
}
|
|
5890
|
-
|
|
5891
|
-
return
|
|
6213
|
+
names2.sort((a, b) => a.localeCompare(b));
|
|
6214
|
+
return names2;
|
|
5892
6215
|
}
|
|
5893
|
-
function buildScopeEntityTypeContent(
|
|
5894
|
-
if (
|
|
6216
|
+
function buildScopeEntityTypeContent(names2) {
|
|
6217
|
+
if (names2.length === 0) {
|
|
5895
6218
|
return HEADER2 + "\nexport type ScopeEntityType = never;\n\nexport const SCOPE_ENTITY_TYPES = [] as const;\n";
|
|
5896
6219
|
}
|
|
5897
|
-
const quoted =
|
|
6220
|
+
const quoted = names2.map((n) => `'${n}'`);
|
|
5898
6221
|
const unionLiteral = quoted.join(" | ");
|
|
5899
6222
|
const tupleLiteral = quoted.join(", ");
|
|
5900
6223
|
return HEADER2 + `
|
|
@@ -6305,7 +6628,7 @@ async function regenerateSubsystemBarrel(opts) {
|
|
|
6305
6628
|
// src/cli/shared/bridge-registry-generator.ts
|
|
6306
6629
|
import fs6 from "fs";
|
|
6307
6630
|
import path9 from "path";
|
|
6308
|
-
import
|
|
6631
|
+
import ts2 from "typescript";
|
|
6309
6632
|
var HEADER4 = `// AUTO-GENERATED by @pattern-stack/codegen. Do not edit.
|
|
6310
6633
|
// Run \`codegen entity new --all\` to refresh.
|
|
6311
6634
|
`;
|
|
@@ -6381,34 +6704,34 @@ function findHandlerFiles(dir) {
|
|
|
6381
6704
|
function extractTriggersFromSourceFile(sourceFile, filePath) {
|
|
6382
6705
|
const triggers = [];
|
|
6383
6706
|
function visit(node) {
|
|
6384
|
-
if (
|
|
6385
|
-
const modifiers =
|
|
6707
|
+
if (ts2.isClassDeclaration(node)) {
|
|
6708
|
+
const modifiers = ts2.canHaveDecorators(node) ? ts2.getDecorators(node) ?? [] : [];
|
|
6386
6709
|
for (const decorator of modifiers) {
|
|
6387
6710
|
const call = decorator.expression;
|
|
6388
|
-
if (!
|
|
6389
|
-
if (!
|
|
6711
|
+
if (!ts2.isCallExpression(call)) continue;
|
|
6712
|
+
if (!ts2.isIdentifier(call.expression)) continue;
|
|
6390
6713
|
if (call.expression.text !== "JobHandler") continue;
|
|
6391
6714
|
const [typeArg, metaArg] = call.arguments;
|
|
6392
|
-
if (!typeArg || !
|
|
6393
|
-
if (!metaArg || !
|
|
6715
|
+
if (!typeArg || !ts2.isStringLiteralLike(typeArg)) continue;
|
|
6716
|
+
if (!metaArg || !ts2.isObjectLiteralExpression(metaArg)) continue;
|
|
6394
6717
|
const jobType = typeArg.text;
|
|
6395
6718
|
const triggersProp = metaArg.properties.find(
|
|
6396
|
-
(p) =>
|
|
6719
|
+
(p) => ts2.isPropertyAssignment(p) && ts2.isIdentifier(p.name) && p.name.text === "triggers"
|
|
6397
6720
|
);
|
|
6398
6721
|
if (!triggersProp) continue;
|
|
6399
|
-
if (!
|
|
6722
|
+
if (!ts2.isArrayLiteralExpression(triggersProp.initializer)) continue;
|
|
6400
6723
|
triggersProp.initializer.elements.forEach((el, index2) => {
|
|
6401
|
-
if (!
|
|
6724
|
+
if (!ts2.isObjectLiteralExpression(el)) return;
|
|
6402
6725
|
const eventProp = el.properties.find(
|
|
6403
|
-
(p) =>
|
|
6726
|
+
(p) => ts2.isPropertyAssignment(p) && ts2.isIdentifier(p.name) && p.name.text === "event"
|
|
6404
6727
|
);
|
|
6405
6728
|
const mapProp = el.properties.find(
|
|
6406
|
-
(p) =>
|
|
6729
|
+
(p) => ts2.isPropertyAssignment(p) && ts2.isIdentifier(p.name) && p.name.text === "map"
|
|
6407
6730
|
);
|
|
6408
6731
|
const whenProp = el.properties.find(
|
|
6409
|
-
(p) =>
|
|
6732
|
+
(p) => ts2.isPropertyAssignment(p) && ts2.isIdentifier(p.name) && p.name.text === "when"
|
|
6410
6733
|
);
|
|
6411
|
-
if (!eventProp || !
|
|
6734
|
+
if (!eventProp || !ts2.isStringLiteralLike(eventProp.initializer)) return;
|
|
6412
6735
|
if (!mapProp) return;
|
|
6413
6736
|
const { line } = sourceFile.getLineAndCharacterOfPosition(el.getStart(sourceFile));
|
|
6414
6737
|
triggers.push({
|
|
@@ -6423,7 +6746,7 @@ function extractTriggersFromSourceFile(sourceFile, filePath) {
|
|
|
6423
6746
|
});
|
|
6424
6747
|
}
|
|
6425
6748
|
}
|
|
6426
|
-
|
|
6749
|
+
ts2.forEachChild(node, visit);
|
|
6427
6750
|
}
|
|
6428
6751
|
visit(sourceFile);
|
|
6429
6752
|
return triggers;
|
|
@@ -6433,13 +6756,13 @@ function scanHandlerFiles(handlersDir) {
|
|
|
6433
6756
|
const out = [];
|
|
6434
6757
|
for (const filePath of files) {
|
|
6435
6758
|
const text2 = fs6.readFileSync(filePath, "utf8");
|
|
6436
|
-
const sourceFile =
|
|
6759
|
+
const sourceFile = ts2.createSourceFile(
|
|
6437
6760
|
filePath,
|
|
6438
6761
|
text2,
|
|
6439
|
-
|
|
6762
|
+
ts2.ScriptTarget.Latest,
|
|
6440
6763
|
/* setParentNodes */
|
|
6441
6764
|
true,
|
|
6442
|
-
|
|
6765
|
+
ts2.ScriptKind.TS
|
|
6443
6766
|
);
|
|
6444
6767
|
out.push(...extractTriggersFromSourceFile(sourceFile, filePath));
|
|
6445
6768
|
}
|
|
@@ -6738,8 +7061,8 @@ function emitTypeImports(imports) {
|
|
|
6738
7061
|
}
|
|
6739
7062
|
const out = [];
|
|
6740
7063
|
for (const specifier of [...grouped.keys()].sort()) {
|
|
6741
|
-
const
|
|
6742
|
-
out.push(`import type { ${
|
|
7064
|
+
const names2 = [...grouped.get(specifier)].sort();
|
|
7065
|
+
out.push(`import type { ${names2.join(", ")} } from '${specifier}';`);
|
|
6743
7066
|
}
|
|
6744
7067
|
return out;
|
|
6745
7068
|
}
|
|
@@ -6752,8 +7075,8 @@ function emitValueImports(imports) {
|
|
|
6752
7075
|
}
|
|
6753
7076
|
const out = [];
|
|
6754
7077
|
for (const specifier of [...grouped.keys()].sort()) {
|
|
6755
|
-
const
|
|
6756
|
-
out.push(`import { ${
|
|
7078
|
+
const names2 = [...grouped.get(specifier)].sort();
|
|
7079
|
+
out.push(`import { ${names2.join(", ")} } from '${specifier}';`);
|
|
6757
7080
|
}
|
|
6758
7081
|
return out;
|
|
6759
7082
|
}
|
|
@@ -6785,13 +7108,13 @@ function buildTokensTs(pattern) {
|
|
|
6785
7108
|
lines.push("");
|
|
6786
7109
|
for (const i of emitTypeImports(typeImports)) lines.push(i);
|
|
6787
7110
|
lines.push("");
|
|
6788
|
-
for (const { names } of registries) {
|
|
6789
|
-
lines.push(`export const ${
|
|
7111
|
+
for (const { names: names2 } of registries) {
|
|
7112
|
+
lines.push(`export const ${names2.tokenConst} = Symbol('${names2.tokenConst}');`);
|
|
6790
7113
|
}
|
|
6791
7114
|
lines.push("");
|
|
6792
|
-
for (const { reg, names } of registries) {
|
|
7115
|
+
for (const { reg, names: names2 } of registries) {
|
|
6793
7116
|
lines.push(
|
|
6794
|
-
`export type ${
|
|
7117
|
+
`export type ${names2.mapType} = Map<${reg.keyType}, ${reg.valueType}>;`
|
|
6795
7118
|
);
|
|
6796
7119
|
}
|
|
6797
7120
|
lines.push("");
|
|
@@ -6805,8 +7128,8 @@ function buildProvidersTs(pattern) {
|
|
|
6805
7128
|
providerImports.push({ specifier: e.providerImport, name: e.provider });
|
|
6806
7129
|
}
|
|
6807
7130
|
}
|
|
6808
|
-
const tokenValueImports = registries.map(({ names }) =>
|
|
6809
|
-
const mapTypeImports = registries.map(({ names }) =>
|
|
7131
|
+
const tokenValueImports = registries.map(({ names: names2 }) => names2.tokenConst);
|
|
7132
|
+
const mapTypeImports = registries.map(({ names: names2 }) => names2.mapType);
|
|
6810
7133
|
const typeImports = [];
|
|
6811
7134
|
for (const { reg } of registries) {
|
|
6812
7135
|
typeImports.push({ specifier: reg.keyTypeImport, name: reg.keyType });
|
|
@@ -6831,24 +7154,24 @@ function buildProvidersTs(pattern) {
|
|
|
6831
7154
|
`export function build${pascal}RegistryProviders(opts?: ${optsType}): Provider[] {`
|
|
6832
7155
|
);
|
|
6833
7156
|
lines.push(" return [");
|
|
6834
|
-
for (const { reg, names } of registries) {
|
|
7157
|
+
for (const { reg, names: names2 } of registries) {
|
|
6835
7158
|
const factoryArgs = reg.entries.map((e, i) => `${camelArg(e.provider, i)}: ${e.provider}`).join(", ");
|
|
6836
7159
|
lines.push(" {");
|
|
6837
|
-
lines.push(` provide: ${
|
|
7160
|
+
lines.push(` provide: ${names2.tokenConst},`);
|
|
6838
7161
|
lines.push(` useFactory: (${factoryArgs}) => {`);
|
|
6839
|
-
lines.push(` const base: ${
|
|
7162
|
+
lines.push(` const base: ${names2.mapType} = new Map<${reg.keyType}, ${reg.valueType}>([`);
|
|
6840
7163
|
for (const [i, e] of reg.entries.entries()) {
|
|
6841
7164
|
lines.push(
|
|
6842
7165
|
` ['${e.key}' as ${reg.keyType}, ${camelArg(e.provider, i)}],`
|
|
6843
7166
|
);
|
|
6844
7167
|
}
|
|
6845
7168
|
lines.push(" ]);");
|
|
6846
|
-
lines.push(` if (opts?.${
|
|
6847
|
-
lines.push(` for (const [k, v] of Object.entries(opts.${
|
|
7169
|
+
lines.push(` if (opts?.${names2.overrideField}) {`);
|
|
7170
|
+
lines.push(` for (const [k, v] of Object.entries(opts.${names2.overrideField})) {`);
|
|
6848
7171
|
lines.push(` if (v !== undefined) base.set(k as ${reg.keyType}, v as ${reg.valueType});`);
|
|
6849
7172
|
lines.push(" }");
|
|
6850
7173
|
lines.push(" }");
|
|
6851
|
-
lines.push(` return base as ${
|
|
7174
|
+
lines.push(` return base as ${names2.mapType};`);
|
|
6852
7175
|
lines.push(" },");
|
|
6853
7176
|
const injectList = reg.entries.map((e) => e.provider).join(", ");
|
|
6854
7177
|
lines.push(` inject: [${injectList}],`);
|
|
@@ -6878,8 +7201,8 @@ function buildDispatcherTs(pattern) {
|
|
|
6878
7201
|
providerImports.push({ specifier: e.providerImport, name: e.provider });
|
|
6879
7202
|
}
|
|
6880
7203
|
}
|
|
6881
|
-
const tokenValues = registries.map(({ names }) =>
|
|
6882
|
-
const mapTypes = registries.map(({ names }) =>
|
|
7204
|
+
const tokenValues = registries.map(({ names: names2 }) => names2.tokenConst);
|
|
7205
|
+
const mapTypes = registries.map(({ names: names2 }) => names2.mapType);
|
|
6883
7206
|
const lines = [];
|
|
6884
7207
|
lines.push(HEADER5.trimEnd());
|
|
6885
7208
|
lines.push("");
|
|
@@ -6897,21 +7220,21 @@ function buildDispatcherTs(pattern) {
|
|
|
6897
7220
|
lines.push("@Injectable()");
|
|
6898
7221
|
lines.push(`export class ${className} {`);
|
|
6899
7222
|
lines.push(" constructor(");
|
|
6900
|
-
for (const { names } of registries) {
|
|
6901
|
-
const fieldName =
|
|
7223
|
+
for (const { names: names2 } of registries) {
|
|
7224
|
+
const fieldName = names2.method === "select" ? "registry" : `${camelFromMethod(names2.method)}Registry`;
|
|
6902
7225
|
lines.push(
|
|
6903
|
-
` @Inject(${
|
|
7226
|
+
` @Inject(${names2.tokenConst}) protected readonly ${fieldName}: ${names2.mapType},`
|
|
6904
7227
|
);
|
|
6905
7228
|
}
|
|
6906
7229
|
lines.push(" ) {}");
|
|
6907
7230
|
lines.push("");
|
|
6908
|
-
for (const { reg, names } of registries) {
|
|
6909
|
-
const fieldName =
|
|
7231
|
+
for (const { reg, names: names2 } of registries) {
|
|
7232
|
+
const fieldName = names2.method === "select" ? "registry" : `${camelFromMethod(names2.method)}Registry`;
|
|
6910
7233
|
for (const e of reg.entries) {
|
|
6911
|
-
lines.push(` ${
|
|
7234
|
+
lines.push(` ${names2.method}(key: '${e.key}'): ${e.provider};`);
|
|
6912
7235
|
}
|
|
6913
|
-
lines.push(` ${
|
|
6914
|
-
lines.push(` ${
|
|
7236
|
+
lines.push(` ${names2.method}(key: ${reg.keyType}): ${reg.valueType};`);
|
|
7237
|
+
lines.push(` ${names2.method}(key: ${reg.keyType}): ${reg.valueType} {`);
|
|
6915
7238
|
lines.push(` const entry = this.${fieldName}.get(key);`);
|
|
6916
7239
|
lines.push(
|
|
6917
7240
|
` if (!entry) throw new ${errorClass}(\`Unknown ${reg.keyType}: \${String(key)}\`);`
|
|
@@ -6953,7 +7276,7 @@ function buildModuleTs(pattern) {
|
|
|
6953
7276
|
typeImports.push({ specifier: reg.keyTypeImport, name: reg.keyType });
|
|
6954
7277
|
typeImports.push({ specifier: reg.valueTypeImport, name: reg.valueType });
|
|
6955
7278
|
}
|
|
6956
|
-
const tokenValues = registries.map(({ names }) =>
|
|
7279
|
+
const tokenValues = registries.map(({ names: names2 }) => names2.tokenConst);
|
|
6957
7280
|
const lines = [];
|
|
6958
7281
|
lines.push(HEADER5.trimEnd());
|
|
6959
7282
|
lines.push("");
|
|
@@ -6964,9 +7287,9 @@ function buildModuleTs(pattern) {
|
|
|
6964
7287
|
lines.push(`import { ${tokenValues.sort().join(", ")} } from './tokens.js';`);
|
|
6965
7288
|
lines.push("");
|
|
6966
7289
|
lines.push(`export interface ${optsType} {`);
|
|
6967
|
-
for (const { reg, names } of registries) {
|
|
7290
|
+
for (const { reg, names: names2 } of registries) {
|
|
6968
7291
|
lines.push(
|
|
6969
|
-
` ${
|
|
7292
|
+
` ${names2.overrideField}?: Partial<Record<${reg.keyType}, ${reg.valueType}>>;`
|
|
6970
7293
|
);
|
|
6971
7294
|
}
|
|
6972
7295
|
lines.push("}");
|
|
@@ -7080,7 +7403,7 @@ import fs8 from "fs";
|
|
|
7080
7403
|
import path11 from "path";
|
|
7081
7404
|
|
|
7082
7405
|
// src/parser/load-events.ts
|
|
7083
|
-
import { basename as basename2, resolve as
|
|
7406
|
+
import { basename as basename2, resolve as resolve4 } from "path";
|
|
7084
7407
|
function loadErrorToIssue2(error) {
|
|
7085
7408
|
const issues = [];
|
|
7086
7409
|
issues.push({
|
|
@@ -7110,7 +7433,7 @@ function stripYamlExt(file) {
|
|
|
7110
7433
|
function loadEvents(eventsDir, entityNames) {
|
|
7111
7434
|
const events = [];
|
|
7112
7435
|
const issues = [];
|
|
7113
|
-
const resolvedDir =
|
|
7436
|
+
const resolvedDir = resolve4(eventsDir);
|
|
7114
7437
|
let files;
|
|
7115
7438
|
try {
|
|
7116
7439
|
files = findYamlFiles(resolvedDir);
|
|
@@ -7752,6 +8075,564 @@ function validateEntityEmits(entities, events) {
|
|
|
7752
8075
|
return issues;
|
|
7753
8076
|
}
|
|
7754
8077
|
|
|
8078
|
+
// src/cli/shared/provider-module-generator.ts
|
|
8079
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync7, statSync as statSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
8080
|
+
import { dirname, isAbsolute as isAbsolute2, join as join11, resolve as resolve5 } from "path";
|
|
8081
|
+
function providerPascalCase(slug) {
|
|
8082
|
+
return slug.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
8083
|
+
}
|
|
8084
|
+
function providerConstantCase(slug) {
|
|
8085
|
+
return slug.replace(/-/g, "_").toUpperCase();
|
|
8086
|
+
}
|
|
8087
|
+
function providerModuleBanner(sourceYaml) {
|
|
8088
|
+
return `// @generated by @pattern-stack/codegen from ${sourceYaml} \u2014 DO NOT EDIT.
|
|
8089
|
+
// Hand edits are overwritten on re-emit. Regenerate with \`bun run codegen\`. To change this provider, edit its \`definitions/providers/*.yaml\`.`;
|
|
8090
|
+
}
|
|
8091
|
+
function generateProviderModule(def, sourceYaml) {
|
|
8092
|
+
const { slug } = def;
|
|
8093
|
+
const Pascal = providerPascalCase(slug);
|
|
8094
|
+
const CONST = providerConstantCase(slug);
|
|
8095
|
+
const strategy = parseImportRef(def.auth.strategy);
|
|
8096
|
+
const client = parseImportRef(def.client.class);
|
|
8097
|
+
const strategyToken = `${CONST}_AUTH_STRATEGY`;
|
|
8098
|
+
const clientToken = `${CONST}_CLIENT`;
|
|
8099
|
+
const surfaces = def.surfaces.join(", ");
|
|
8100
|
+
const title = def.display_name ? `${def.display_name} (\`${slug}\`)` : `\`${slug}\``;
|
|
8101
|
+
return `${providerModuleBanner(sourceYaml)}
|
|
8102
|
+
/**
|
|
8103
|
+
* Provider module for ${title}.
|
|
8104
|
+
*
|
|
8105
|
+
* Surfaces: ${surfaces}.
|
|
8106
|
+
*
|
|
8107
|
+
* Wires the declared auth strategy and API client under the provider-specific
|
|
8108
|
+
* DI tokens \`${strategyToken}\` and \`${clientToken}\`. Per the auth subsystem
|
|
8109
|
+
* contract, strategies are registered under provider-specific tokens rather
|
|
8110
|
+
* than a single \`AUTH_STRATEGY\`. Registry aggregation (RFC-0001 \xA73) is
|
|
8111
|
+
* emitted by a later codegen step.
|
|
8112
|
+
*/
|
|
8113
|
+
import { Module } from '@nestjs/common';
|
|
8114
|
+
import { ${strategy.exportName} } from '${strategy.path}';
|
|
8115
|
+
import { ${client.exportName} } from '${client.path}';
|
|
8116
|
+
|
|
8117
|
+
/** DI token for the \`${slug}\` provider's auth strategy. */
|
|
8118
|
+
export const ${strategyToken} = Symbol('${strategyToken}');
|
|
8119
|
+
/** DI token for the \`${slug}\` provider's API client. */
|
|
8120
|
+
export const ${clientToken} = Symbol('${clientToken}');
|
|
8121
|
+
|
|
8122
|
+
@Module({
|
|
8123
|
+
providers: [
|
|
8124
|
+
${strategy.exportName},
|
|
8125
|
+
${client.exportName},
|
|
8126
|
+
{ provide: ${strategyToken}, useExisting: ${strategy.exportName} },
|
|
8127
|
+
{ provide: ${clientToken}, useExisting: ${client.exportName} },
|
|
8128
|
+
],
|
|
8129
|
+
exports: [
|
|
8130
|
+
${strategy.exportName},
|
|
8131
|
+
${client.exportName},
|
|
8132
|
+
${strategyToken},
|
|
8133
|
+
${clientToken},
|
|
8134
|
+
],
|
|
8135
|
+
})
|
|
8136
|
+
export class ${Pascal}ProviderModule {}
|
|
8137
|
+
`;
|
|
8138
|
+
}
|
|
8139
|
+
function resolveTsconfigAliases(consumerRoot) {
|
|
8140
|
+
const tsconfigPath = join11(consumerRoot, "tsconfig.json");
|
|
8141
|
+
if (!existsSync9(tsconfigPath)) return null;
|
|
8142
|
+
let parsed;
|
|
8143
|
+
try {
|
|
8144
|
+
parsed = JSON.parse(stripJsonComments(readFileSync7(tsconfigPath, "utf-8")));
|
|
8145
|
+
} catch {
|
|
8146
|
+
return null;
|
|
8147
|
+
}
|
|
8148
|
+
const compilerOptions = parsed.compilerOptions ?? {};
|
|
8149
|
+
const baseUrl = compilerOptions.baseUrl ?? ".";
|
|
8150
|
+
const sourceRoot = isAbsolute2(baseUrl) ? baseUrl : resolve5(consumerRoot, baseUrl);
|
|
8151
|
+
const aliases = {};
|
|
8152
|
+
for (const [pattern, targets] of Object.entries(compilerOptions.paths ?? {})) {
|
|
8153
|
+
if (!Array.isArray(targets) || targets.length === 0) continue;
|
|
8154
|
+
const aliasKey = pattern.replace(/\/\*$/, "");
|
|
8155
|
+
const target = targets[0].replace(/\/\*$/, "");
|
|
8156
|
+
aliases[aliasKey] = isAbsolute2(target) ? target : resolve5(sourceRoot, target);
|
|
8157
|
+
}
|
|
8158
|
+
return { sourceRoot, aliases };
|
|
8159
|
+
}
|
|
8160
|
+
function stripJsonComments(input) {
|
|
8161
|
+
return input.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1");
|
|
8162
|
+
}
|
|
8163
|
+
function generateProviderModules(opts) {
|
|
8164
|
+
const base = {
|
|
8165
|
+
providersDir: opts.providersDir,
|
|
8166
|
+
skipped: false,
|
|
8167
|
+
written: [],
|
|
8168
|
+
loadFailures: [],
|
|
8169
|
+
issues: []
|
|
8170
|
+
};
|
|
8171
|
+
if (!existsSync9(opts.providersDir) || !statSync5(opts.providersDir).isDirectory()) {
|
|
8172
|
+
return { ...base, skipped: true };
|
|
8173
|
+
}
|
|
8174
|
+
const files = findYamlFiles(opts.providersDir);
|
|
8175
|
+
if (files.length === 0) return { ...base, skipped: true };
|
|
8176
|
+
const { successes, failures } = loadProvidersFromYaml(files);
|
|
8177
|
+
const loaded = successes.map((s) => ({
|
|
8178
|
+
definition: s.definition,
|
|
8179
|
+
filePath: s.filePath
|
|
8180
|
+
}));
|
|
8181
|
+
const issues = failures.map((f) => ({
|
|
8182
|
+
severity: "error",
|
|
8183
|
+
type: "provider_load_failed",
|
|
8184
|
+
message: `${f.error}${f.details?.length ? ` \u2014 ${f.details.join("; ")}` : ""}`,
|
|
8185
|
+
path: f.filePath
|
|
8186
|
+
}));
|
|
8187
|
+
issues.push(
|
|
8188
|
+
...validateProviders(loaded, {
|
|
8189
|
+
entitySurfaces: opts.entitySurfaces,
|
|
8190
|
+
sourceRoot: opts.sourceRoot,
|
|
8191
|
+
aliases: opts.aliases,
|
|
8192
|
+
skipImportCheck: opts.skipImportCheck
|
|
8193
|
+
})
|
|
8194
|
+
);
|
|
8195
|
+
if (issues.some((i) => i.severity === "error")) {
|
|
8196
|
+
return { ...base, loadFailures: failures, issues };
|
|
8197
|
+
}
|
|
8198
|
+
const written = [];
|
|
8199
|
+
for (const { definition, filePath } of loaded) {
|
|
8200
|
+
const sourceYaml = relativeSource(filePath);
|
|
8201
|
+
const content = generateProviderModule(definition, sourceYaml);
|
|
8202
|
+
const outPath = join11(
|
|
8203
|
+
opts.outputRoot,
|
|
8204
|
+
definition.slug,
|
|
8205
|
+
`${definition.slug}.provider.module.ts`
|
|
8206
|
+
);
|
|
8207
|
+
if (!opts.dryRun) {
|
|
8208
|
+
writeIfChanged(outPath, content);
|
|
8209
|
+
}
|
|
8210
|
+
written.push(outPath);
|
|
8211
|
+
}
|
|
8212
|
+
return { ...base, written, loadFailures: failures, issues };
|
|
8213
|
+
}
|
|
8214
|
+
function writeIfChanged(outPath, content) {
|
|
8215
|
+
if (existsSync9(outPath) && readFileSync7(outPath, "utf-8") === content) return;
|
|
8216
|
+
mkdirSync2(dirname(outPath), { recursive: true });
|
|
8217
|
+
writeFileSync2(outPath, content);
|
|
8218
|
+
}
|
|
8219
|
+
function relativeSource(filePath) {
|
|
8220
|
+
const marker = "definitions/providers/";
|
|
8221
|
+
const idx = filePath.lastIndexOf(marker);
|
|
8222
|
+
if (idx !== -1) return filePath.slice(idx);
|
|
8223
|
+
const slash = filePath.lastIndexOf("/");
|
|
8224
|
+
return slash === -1 ? filePath : `definitions/providers/${filePath.slice(slash + 1)}`;
|
|
8225
|
+
}
|
|
8226
|
+
|
|
8227
|
+
// src/cli/shared/adapter-emission-generator.ts
|
|
8228
|
+
import {
|
|
8229
|
+
existsSync as existsSync10,
|
|
8230
|
+
mkdirSync as mkdirSync3,
|
|
8231
|
+
readFileSync as readFileSync8,
|
|
8232
|
+
statSync as statSync6,
|
|
8233
|
+
writeFileSync as writeFileSync3
|
|
8234
|
+
} from "fs";
|
|
8235
|
+
import { dirname as dirname2, join as join12 } from "path";
|
|
8236
|
+
var SURFACE_REGISTRY = {
|
|
8237
|
+
crm: {
|
|
8238
|
+
packageName: "@pattern-stack/codegen-crm",
|
|
8239
|
+
portType: "CrmPort",
|
|
8240
|
+
capabilitiesType: "CrmCapabilities",
|
|
8241
|
+
noCapsConst: "NO_CRM_CAPABILITIES",
|
|
8242
|
+
l2Ports: [
|
|
8243
|
+
{
|
|
8244
|
+
prop: "fields",
|
|
8245
|
+
type: "IFieldDefinitionReader",
|
|
8246
|
+
method: "list",
|
|
8247
|
+
params: "(_integrationId, _entity)",
|
|
8248
|
+
capFlag: "fieldDefinitions"
|
|
8249
|
+
},
|
|
8250
|
+
{
|
|
8251
|
+
prop: "picklists",
|
|
8252
|
+
type: "IPicklistReader",
|
|
8253
|
+
method: "values",
|
|
8254
|
+
params: "(_integrationId, _entity, _fieldId)",
|
|
8255
|
+
capFlag: "picklists"
|
|
8256
|
+
},
|
|
8257
|
+
{
|
|
8258
|
+
prop: "associations",
|
|
8259
|
+
type: "IAssociationReader",
|
|
8260
|
+
method: "list",
|
|
8261
|
+
params: "(_integrationId, _fromEntity, _fromId, _toEntity)",
|
|
8262
|
+
capFlag: "associations"
|
|
8263
|
+
}
|
|
8264
|
+
]
|
|
8265
|
+
},
|
|
8266
|
+
// Interaction surfaces (#416) are incremental-read with no L2 sub-ports — the
|
|
8267
|
+
// port composes L1 (auth + sources) + a capabilities descriptor whose only
|
|
8268
|
+
// field is `entities`; reads go through the change-source registry. So
|
|
8269
|
+
// `l2Ports: []` and the scaffold emits no L2 readers/stubs/capability flags.
|
|
8270
|
+
calendar: {
|
|
8271
|
+
packageName: "@pattern-stack/codegen-calendar",
|
|
8272
|
+
portType: "CalendarPort",
|
|
8273
|
+
capabilitiesType: "CalendarCapabilities",
|
|
8274
|
+
noCapsConst: "NO_CALENDAR_CAPABILITIES",
|
|
8275
|
+
l2Ports: []
|
|
8276
|
+
},
|
|
8277
|
+
mail: {
|
|
8278
|
+
packageName: "@pattern-stack/codegen-mail",
|
|
8279
|
+
portType: "MailPort",
|
|
8280
|
+
capabilitiesType: "MailCapabilities",
|
|
8281
|
+
noCapsConst: "NO_MAIL_CAPABILITIES",
|
|
8282
|
+
l2Ports: []
|
|
8283
|
+
},
|
|
8284
|
+
transcript: {
|
|
8285
|
+
packageName: "@pattern-stack/codegen-transcript",
|
|
8286
|
+
portType: "TranscriptPort",
|
|
8287
|
+
capabilitiesType: "TranscriptCapabilities",
|
|
8288
|
+
noCapsConst: "NO_TRANSCRIPT_CAPABILITIES",
|
|
8289
|
+
l2Ports: []
|
|
8290
|
+
}
|
|
8291
|
+
};
|
|
8292
|
+
var SCAFFOLD_SENTINEL = "// <CODEGEN-SCAFFOLD-V1>";
|
|
8293
|
+
function generatedBanner(sourceDesc) {
|
|
8294
|
+
return `// @generated by @pattern-stack/codegen from ${sourceDesc} \u2014 DO NOT EDIT.
|
|
8295
|
+
// Hand edits are overwritten on re-emit. Regenerate with \`bun run codegen\`.`;
|
|
8296
|
+
}
|
|
8297
|
+
function collectEntitiesBySurface(entities) {
|
|
8298
|
+
const bySurface = /* @__PURE__ */ new Map();
|
|
8299
|
+
for (const e of entities) {
|
|
8300
|
+
if (!e.surface) continue;
|
|
8301
|
+
const list = bySurface.get(e.surface) ?? [];
|
|
8302
|
+
list.push(e.entity.name);
|
|
8303
|
+
bySurface.set(e.surface, list);
|
|
8304
|
+
}
|
|
8305
|
+
for (const [surface, list] of bySurface) {
|
|
8306
|
+
bySurface.set(surface, [...list].sort());
|
|
8307
|
+
}
|
|
8308
|
+
return bySurface;
|
|
8309
|
+
}
|
|
8310
|
+
function names(providerSlug, surface) {
|
|
8311
|
+
const providerPascal = providerPascalCase(providerSlug);
|
|
8312
|
+
const providerConst = providerConstantCase(providerSlug);
|
|
8313
|
+
const surfacePascal = providerPascalCase(surface);
|
|
8314
|
+
const surfaceConst = providerConstantCase(surface);
|
|
8315
|
+
return {
|
|
8316
|
+
providerPascal,
|
|
8317
|
+
providerConst,
|
|
8318
|
+
surfacePascal,
|
|
8319
|
+
surfaceConst,
|
|
8320
|
+
adapterClass: `${providerPascal}${surfacePascal}Adapter`,
|
|
8321
|
+
adapterModuleClass: `${providerPascal}${surfacePascal}AdapterModule`,
|
|
8322
|
+
providerModuleClass: `${providerPascal}ProviderModule`,
|
|
8323
|
+
aggregatorClass: `${surfacePascal}AdaptersModule`,
|
|
8324
|
+
strategyToken: `${providerConst}_AUTH_STRATEGY`,
|
|
8325
|
+
clientToken: `${providerConst}_CLIENT`,
|
|
8326
|
+
contributionsToken: `${surfaceConst}_ADAPTER_CONTRIBUTIONS`,
|
|
8327
|
+
entitySourcesToken: `${surfaceConst}_ENTITY_SOURCES`
|
|
8328
|
+
};
|
|
8329
|
+
}
|
|
8330
|
+
function generateAdapterScaffold(def, surface, entities) {
|
|
8331
|
+
const spec = SURFACE_REGISTRY[surface];
|
|
8332
|
+
if (!spec) throw new Error(`no surface package for '${surface}'`);
|
|
8333
|
+
const n = names(def.slug, surface);
|
|
8334
|
+
const client = parseImportRef(def.client.class);
|
|
8335
|
+
const entitiesLiteral = entities.length ? `[${entities.map((e) => `'${e}'`).join(", ")}]` : "[]";
|
|
8336
|
+
const surfaceTypeImports = [
|
|
8337
|
+
spec.portType,
|
|
8338
|
+
...spec.l2Ports.map((p) => p.type),
|
|
8339
|
+
spec.capabilitiesType
|
|
8340
|
+
].map((t) => ` ${t},`).join("\n");
|
|
8341
|
+
const capabilityBody = [
|
|
8342
|
+
` ...${spec.noCapsConst},`,
|
|
8343
|
+
...spec.l2Ports.map((p) => ` ${p.capFlag}: true,`),
|
|
8344
|
+
` entities: ${entitiesLiteral},`
|
|
8345
|
+
].join("\n");
|
|
8346
|
+
const l2Members = spec.l2Ports.map(
|
|
8347
|
+
(p) => ` /** L2 \u2014 fill in the provider-specific implementation. */
|
|
8348
|
+
readonly ${p.prop}: ${p.type} = {
|
|
8349
|
+
${p.method}: async ${p.params} => {
|
|
8350
|
+
throw new Error('not implemented: ${n.adapterClass}.${p.prop}.${p.method}');
|
|
8351
|
+
},
|
|
8352
|
+
};`
|
|
8353
|
+
).join("\n\n");
|
|
8354
|
+
const l2Section = l2Members ? `
|
|
8355
|
+
${l2Members}
|
|
8356
|
+
` : "";
|
|
8357
|
+
return `${SCAFFOLD_SENTINEL}
|
|
8358
|
+
// Scaffolded once by @pattern-stack/codegen, then author-owned. Re-running
|
|
8359
|
+
// codegen detects the sentinel above and SKIPS this file \u2014 your edits are safe.
|
|
8360
|
+
// Source: definitions/providers/${def.slug}.yaml (surface: ${surface}).
|
|
8361
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
8362
|
+
import type {
|
|
8363
|
+
${surfaceTypeImports}
|
|
8364
|
+
} from '${spec.packageName}';
|
|
8365
|
+
import { ${spec.noCapsConst} } from '${spec.packageName}';
|
|
8366
|
+
import type {
|
|
8367
|
+
IAuthStrategy,
|
|
8368
|
+
IChangeSource,
|
|
8369
|
+
IEntityChangeSourceRegistry,
|
|
8370
|
+
} from '@pattern-stack/codegen/subsystems';
|
|
8371
|
+
import type { ${client.exportName} } from '${client.path}';
|
|
8372
|
+
import { ${n.strategyToken}, ${n.clientToken} } from '../../../providers/${def.slug}/${def.slug}.provider.module';
|
|
8373
|
+
import { ${n.entitySourcesToken} } from '../../${surface}-adapters.tokens';
|
|
8374
|
+
|
|
8375
|
+
@Injectable()
|
|
8376
|
+
export class ${n.adapterClass} implements ${spec.portType} {
|
|
8377
|
+
/** Declared capabilities. \`entities\` derives from \`surface: ${surface}\` entity YAML. */
|
|
8378
|
+
readonly capabilities: ${spec.capabilitiesType} = {
|
|
8379
|
+
${capabilityBody}
|
|
8380
|
+
};
|
|
8381
|
+
|
|
8382
|
+
constructor(
|
|
8383
|
+
@Inject(${n.strategyToken}) readonly auth: IAuthStrategy,
|
|
8384
|
+
@Inject(${n.clientToken}) private readonly client: ${client.exportName},
|
|
8385
|
+
@Inject(${n.entitySourcesToken}) readonly sources: IEntityChangeSourceRegistry,
|
|
8386
|
+
) {}
|
|
8387
|
+
${l2Section}
|
|
8388
|
+
/**
|
|
8389
|
+
* Per-entity change sources this adapter contributes to the ${surface}
|
|
8390
|
+
* registry (ADR-033 \`buildChangeSource\`), keyed by entity name. The
|
|
8391
|
+
* surface aggregator folds these into the \`IEntityChangeSourceRegistry\`
|
|
8392
|
+
* bound under \`${n.entitySourcesToken}\`. Author-owned \u2014 populate one entry
|
|
8393
|
+
* per entity in \`capabilities.entities\`.
|
|
8394
|
+
*/
|
|
8395
|
+
readonly changeSources: Record<string, IChangeSource<unknown>> = {};
|
|
8396
|
+
|
|
8397
|
+
// surface-only methods (optional on ${spec.portType}): add here
|
|
8398
|
+
}
|
|
8399
|
+
`;
|
|
8400
|
+
}
|
|
8401
|
+
function generateAdapterModule(def, surface) {
|
|
8402
|
+
const n = names(def.slug, surface);
|
|
8403
|
+
return `${generatedBanner(`definitions/providers/${def.slug}.yaml (surface: ${surface})`)}
|
|
8404
|
+
import { Module } from '@nestjs/common';
|
|
8405
|
+
import { ${n.providerModuleClass} } from '../../../providers/${def.slug}/${def.slug}.provider.module';
|
|
8406
|
+
import { ${n.adapterClass} } from './${def.slug}-${surface}.adapter';
|
|
8407
|
+
|
|
8408
|
+
@Module({
|
|
8409
|
+
imports: [${n.providerModuleClass}],
|
|
8410
|
+
providers: [${n.adapterClass}],
|
|
8411
|
+
exports: [${n.adapterClass}],
|
|
8412
|
+
})
|
|
8413
|
+
export class ${n.adapterModuleClass} {}
|
|
8414
|
+
`;
|
|
8415
|
+
}
|
|
8416
|
+
function generateAdaptersBarrel(surface, providerSlugs) {
|
|
8417
|
+
const lines = [...providerSlugs].sort().map((slug) => {
|
|
8418
|
+
const n = names(slug, surface);
|
|
8419
|
+
return `export { ${n.adapterModuleClass} } from './${slug}/${slug}-${surface}.adapter.module';`;
|
|
8420
|
+
}).join("\n");
|
|
8421
|
+
return `${generatedBanner(`definitions/providers/*.yaml (surface: ${surface})`)}
|
|
8422
|
+
${lines}
|
|
8423
|
+
`;
|
|
8424
|
+
}
|
|
8425
|
+
function generateSurfaceTokens(surface) {
|
|
8426
|
+
const n = names("__placeholder__", surface);
|
|
8427
|
+
return `${generatedBanner(`surface: ${surface}`)}
|
|
8428
|
+
import type { IChangeSource } from '@pattern-stack/codegen/subsystems';
|
|
8429
|
+
|
|
8430
|
+
/** The assembled list of every ${surface} adapter's contribution. */
|
|
8431
|
+
export const ${n.contributionsToken} = Symbol.for('@app/integrations/${surface}.adapter-contributions');
|
|
8432
|
+
|
|
8433
|
+
/** Resolved registry token \u2014 resolves to a C7 IEntityChangeSourceRegistry. */
|
|
8434
|
+
export const ${n.entitySourcesToken} = Symbol.for('@app/integrations/${surface}.entity-sources');
|
|
8435
|
+
|
|
8436
|
+
/** One provider-adapter's contribution to the surface registry. */
|
|
8437
|
+
export interface AdapterContribution {
|
|
8438
|
+
/** Provider slug. */
|
|
8439
|
+
provider: string;
|
|
8440
|
+
/** Entities this provider serves on this surface \u2192 their change sources. */
|
|
8441
|
+
sources: Record<string, IChangeSource<unknown>>;
|
|
8442
|
+
}
|
|
8443
|
+
`;
|
|
8444
|
+
}
|
|
8445
|
+
function generateSurfaceAggregator(surface, providerSlugs) {
|
|
8446
|
+
const n = names("__placeholder__", surface);
|
|
8447
|
+
const slugs = [...providerSlugs].sort();
|
|
8448
|
+
const per = slugs.map((slug) => names(slug, surface));
|
|
8449
|
+
const moduleClasses = per.map((p) => p.adapterModuleClass);
|
|
8450
|
+
const moduleImport = `import {
|
|
8451
|
+
${moduleClasses.join(",\n ")},
|
|
8452
|
+
} from './adapters';`;
|
|
8453
|
+
const adapterImports = slugs.map((slug) => {
|
|
8454
|
+
const p = names(slug, surface);
|
|
8455
|
+
return `import { ${p.adapterClass} } from './adapters/${slug}/${slug}-${surface}.adapter';`;
|
|
8456
|
+
}).join("\n");
|
|
8457
|
+
const contributionEntries = slugs.map((slug) => {
|
|
8458
|
+
const p = names(slug, surface);
|
|
8459
|
+
return ` { provider: '${slug}', sources: ${lowerFirst(p.adapterClass)}.changeSources },`;
|
|
8460
|
+
}).join("\n");
|
|
8461
|
+
const injectParams = slugs.map((slug) => {
|
|
8462
|
+
const p = names(slug, surface);
|
|
8463
|
+
return `${lowerFirst(p.adapterClass)}: ${p.adapterClass}`;
|
|
8464
|
+
}).join(", ");
|
|
8465
|
+
const injectTokens = per.map((p) => p.adapterClass).join(", ");
|
|
8466
|
+
return `${generatedBanner(`surface: ${surface}`)}
|
|
8467
|
+
import { Module } from '@nestjs/common';
|
|
8468
|
+
import {
|
|
8469
|
+
MemoryEntityChangeSourceRegistry,
|
|
8470
|
+
type IChangeSource,
|
|
8471
|
+
type IEntityChangeSourceRegistry,
|
|
8472
|
+
} from '@pattern-stack/codegen/subsystems';
|
|
8473
|
+
${moduleImport}
|
|
8474
|
+
${adapterImports}
|
|
8475
|
+
import {
|
|
8476
|
+
${n.contributionsToken},
|
|
8477
|
+
${n.entitySourcesToken},
|
|
8478
|
+
type AdapterContribution,
|
|
8479
|
+
} from './${surface}-adapters.tokens';
|
|
8480
|
+
|
|
8481
|
+
/**
|
|
8482
|
+
* Fold every adapter contribution into one entity-keyed registry. Each entity
|
|
8483
|
+
* resolves to exactly one change source; two providers serving the same entity
|
|
8484
|
+
* is an ambiguous-source boot error.
|
|
8485
|
+
*/
|
|
8486
|
+
function provide${n.surfacePascal}EntitySources(
|
|
8487
|
+
contribs: AdapterContribution[],
|
|
8488
|
+
): IEntityChangeSourceRegistry {
|
|
8489
|
+
const merged = new Map<string, IChangeSource<unknown>>();
|
|
8490
|
+
const owner = new Map<string, string>();
|
|
8491
|
+
for (const contrib of contribs ?? []) {
|
|
8492
|
+
for (const [entity, source] of Object.entries(contrib.sources)) {
|
|
8493
|
+
const prior = owner.get(entity);
|
|
8494
|
+
if (prior !== undefined) {
|
|
8495
|
+
throw new Error(
|
|
8496
|
+
\`entity '\${entity}' is served by both '\${prior}' and '\${contrib.provider}' \u2014 ambiguous change source\`,
|
|
8497
|
+
);
|
|
8498
|
+
}
|
|
8499
|
+
owner.set(entity, contrib.provider);
|
|
8500
|
+
merged.set(entity, source);
|
|
8501
|
+
}
|
|
8502
|
+
}
|
|
8503
|
+
return new MemoryEntityChangeSourceRegistry(merged);
|
|
8504
|
+
}
|
|
8505
|
+
|
|
8506
|
+
@Module({
|
|
8507
|
+
imports: [${moduleClasses.join(", ")}],
|
|
8508
|
+
providers: [
|
|
8509
|
+
{
|
|
8510
|
+
provide: ${n.contributionsToken},
|
|
8511
|
+
useFactory: (${injectParams}): AdapterContribution[] => [
|
|
8512
|
+
${contributionEntries}
|
|
8513
|
+
],
|
|
8514
|
+
inject: [${injectTokens}],
|
|
8515
|
+
},
|
|
8516
|
+
{
|
|
8517
|
+
provide: ${n.entitySourcesToken},
|
|
8518
|
+
useFactory: (contributions: AdapterContribution[]) =>
|
|
8519
|
+
provide${n.surfacePascal}EntitySources(contributions),
|
|
8520
|
+
inject: [${n.contributionsToken}],
|
|
8521
|
+
},
|
|
8522
|
+
],
|
|
8523
|
+
exports: [${n.entitySourcesToken}, ${n.contributionsToken}],
|
|
8524
|
+
})
|
|
8525
|
+
export class ${n.aggregatorClass} {}
|
|
8526
|
+
`;
|
|
8527
|
+
}
|
|
8528
|
+
function lowerFirst(s) {
|
|
8529
|
+
return s.length ? s[0].toLowerCase() + s.slice(1) : s;
|
|
8530
|
+
}
|
|
8531
|
+
function generateTypedView(surface, providerSlugs, entities) {
|
|
8532
|
+
const surfacePascal = providerPascalCase(surface);
|
|
8533
|
+
const slugs = [...providerSlugs].sort();
|
|
8534
|
+
const ents = [...entities].sort();
|
|
8535
|
+
const providerUnion = slugs.length ? slugs.map((s) => `'${s}'`).join(" | ") : "never";
|
|
8536
|
+
const entityUnion = ents.length ? ents.map((e) => `'${e}'`).join(" | ") : "never";
|
|
8537
|
+
const mapEntries = slugs.map((s) => ` ${jsKey(s)}: ${surfacePascal}Entity;`).join("\n");
|
|
8538
|
+
return `${generatedBanner(`surface: ${surface}`)}
|
|
8539
|
+
/**
|
|
8540
|
+
* Per-consumer typed view for the \`${surface}\` surface. Surface-scoped unions
|
|
8541
|
+
* + a (provider, entity) validity map for compile-time-checked consumer
|
|
8542
|
+
* use-cases. Replaces ADR-033.2's per-entity provider tuples (RFC-0001 \xA75/\xA78).
|
|
8543
|
+
*/
|
|
8544
|
+
|
|
8545
|
+
/** Providers whose \`surfaces[]\` include \`${surface}\`. */
|
|
8546
|
+
export type ${surfacePascal}Provider = ${providerUnion};
|
|
8547
|
+
|
|
8548
|
+
/** Entities declared with \`surface: ${surface}\`. */
|
|
8549
|
+
export type ${surfacePascal}Entity = ${entityUnion};
|
|
8550
|
+
|
|
8551
|
+
/** Valid entities per provider on this surface. */
|
|
8552
|
+
export interface ${surfacePascal}ProviderEntities {
|
|
8553
|
+
${mapEntries || " // no providers serve this surface yet"}
|
|
8554
|
+
}
|
|
8555
|
+
`;
|
|
8556
|
+
}
|
|
8557
|
+
function jsKey(key) {
|
|
8558
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : `'${key}'`;
|
|
8559
|
+
}
|
|
8560
|
+
function emitAdapters(opts) {
|
|
8561
|
+
const result = {
|
|
8562
|
+
written: [],
|
|
8563
|
+
scaffoldsWritten: [],
|
|
8564
|
+
scaffoldsSkipped: [],
|
|
8565
|
+
skippedSurfaces: []
|
|
8566
|
+
};
|
|
8567
|
+
const entitiesBySurface = collectEntitiesBySurface(opts.entities);
|
|
8568
|
+
const bySurface = /* @__PURE__ */ new Map();
|
|
8569
|
+
for (const { definition } of opts.providers) {
|
|
8570
|
+
for (const surface of definition.surfaces) {
|
|
8571
|
+
if (!SURFACE_REGISTRY[surface]) {
|
|
8572
|
+
result.skippedSurfaces.push({
|
|
8573
|
+
provider: definition.slug,
|
|
8574
|
+
surface,
|
|
8575
|
+
reason: `no surface package for '${surface}' yet \u2014 add codegen-${surface} (Track C) to emit its adapters`
|
|
8576
|
+
});
|
|
8577
|
+
continue;
|
|
8578
|
+
}
|
|
8579
|
+
const list = bySurface.get(surface) ?? [];
|
|
8580
|
+
list.push(definition.slug);
|
|
8581
|
+
bySurface.set(surface, list);
|
|
8582
|
+
}
|
|
8583
|
+
}
|
|
8584
|
+
const defBySlug = new Map(opts.providers.map((p) => [p.definition.slug, p.definition]));
|
|
8585
|
+
for (const [surface, slugs] of bySurface) {
|
|
8586
|
+
const surfaceDir = join12(opts.outputRoot, surface);
|
|
8587
|
+
const adaptersDir = join12(surfaceDir, "adapters");
|
|
8588
|
+
for (const slug of slugs) {
|
|
8589
|
+
const def = defBySlug.get(slug);
|
|
8590
|
+
const providerDir = join12(adaptersDir, slug);
|
|
8591
|
+
const scaffoldPath = join12(providerDir, `${slug}-${surface}.adapter.ts`);
|
|
8592
|
+
const modulePath = join12(providerDir, `${slug}-${surface}.adapter.module.ts`);
|
|
8593
|
+
if (existsSync10(scaffoldPath)) {
|
|
8594
|
+
result.scaffoldsSkipped.push(scaffoldPath);
|
|
8595
|
+
} else {
|
|
8596
|
+
const content = generateAdapterScaffold(
|
|
8597
|
+
def,
|
|
8598
|
+
surface,
|
|
8599
|
+
entitiesBySurface.get(surface) ?? []
|
|
8600
|
+
);
|
|
8601
|
+
if (!opts.dryRun) writeFile(scaffoldPath, content);
|
|
8602
|
+
result.scaffoldsWritten.push(scaffoldPath);
|
|
8603
|
+
}
|
|
8604
|
+
const moduleContent = generateAdapterModule(def, surface);
|
|
8605
|
+
if (!opts.dryRun) writeIfChanged2(modulePath, moduleContent);
|
|
8606
|
+
result.written.push(modulePath);
|
|
8607
|
+
}
|
|
8608
|
+
const barrelPath = join12(adaptersDir, "index.ts");
|
|
8609
|
+
const tokensPath = join12(surfaceDir, `${surface}-adapters.tokens.ts`);
|
|
8610
|
+
const aggregatorPath = join12(surfaceDir, `${surface}-adapters.module.ts`);
|
|
8611
|
+
const typedViewPath = join12(surfaceDir, "types.generated.ts");
|
|
8612
|
+
const files = [
|
|
8613
|
+
[barrelPath, generateAdaptersBarrel(surface, slugs)],
|
|
8614
|
+
[tokensPath, generateSurfaceTokens(surface)],
|
|
8615
|
+
[aggregatorPath, generateSurfaceAggregator(surface, slugs)],
|
|
8616
|
+
[typedViewPath, generateTypedView(surface, slugs, entitiesBySurface.get(surface) ?? [])]
|
|
8617
|
+
];
|
|
8618
|
+
for (const [path34, content] of files) {
|
|
8619
|
+
if (!opts.dryRun) writeIfChanged2(path34, content);
|
|
8620
|
+
result.written.push(path34);
|
|
8621
|
+
}
|
|
8622
|
+
}
|
|
8623
|
+
return result;
|
|
8624
|
+
}
|
|
8625
|
+
function writeFile(outPath, content) {
|
|
8626
|
+
mkdirSync3(dirname2(outPath), { recursive: true });
|
|
8627
|
+
writeFileSync3(outPath, content);
|
|
8628
|
+
}
|
|
8629
|
+
function writeIfChanged2(outPath, content) {
|
|
8630
|
+
if (existsSync10(outPath) && statSync6(outPath).isFile() && readFileSync8(outPath, "utf-8") === content) {
|
|
8631
|
+
return;
|
|
8632
|
+
}
|
|
8633
|
+
writeFile(outPath, content);
|
|
8634
|
+
}
|
|
8635
|
+
|
|
7755
8636
|
// src/cli/shared/events-path.ts
|
|
7756
8637
|
import path12 from "path";
|
|
7757
8638
|
var FALLBACK = "events";
|
|
@@ -8261,6 +9142,92 @@ var EntityNewCommand = class extends Command2 {
|
|
|
8261
9142
|
}
|
|
8262
9143
|
}
|
|
8263
9144
|
}
|
|
9145
|
+
let providerResult = null;
|
|
9146
|
+
try {
|
|
9147
|
+
const providersDir = ctx.config?.paths?.providers != null ? path13.resolve(
|
|
9148
|
+
ctx.cwd,
|
|
9149
|
+
ctx.config.paths.providers
|
|
9150
|
+
) : path13.resolve(ctx.cwd, "definitions/providers");
|
|
9151
|
+
const providerOutputRoot = path13.resolve(
|
|
9152
|
+
ctx.cwd,
|
|
9153
|
+
backendSrcForHandlers,
|
|
9154
|
+
"integrations/providers"
|
|
9155
|
+
);
|
|
9156
|
+
const entitySurfaces = fs9.existsSync(entitiesDir) ? collectEntitySurfaces(
|
|
9157
|
+
loadEntitiesFromYaml(findYamlFiles(entitiesDir)).successes.map(
|
|
9158
|
+
(s) => s.definition
|
|
9159
|
+
)
|
|
9160
|
+
) : /* @__PURE__ */ new Set();
|
|
9161
|
+
const tsAliases = resolveTsconfigAliases(ctx.cwd);
|
|
9162
|
+
providerResult = generateProviderModules({
|
|
9163
|
+
providersDir,
|
|
9164
|
+
outputRoot: providerOutputRoot,
|
|
9165
|
+
entitySurfaces,
|
|
9166
|
+
sourceRoot: tsAliases?.sourceRoot,
|
|
9167
|
+
aliases: tsAliases?.aliases,
|
|
9168
|
+
skipImportCheck: tsAliases === null
|
|
9169
|
+
});
|
|
9170
|
+
if (!providerResult.skipped && !isJsonMode()) {
|
|
9171
|
+
for (const issue of providerResult.issues) {
|
|
9172
|
+
printError(`provider codegen: ${issue.message}`);
|
|
9173
|
+
}
|
|
9174
|
+
if (providerResult.issues.length === 0) {
|
|
9175
|
+
printInfo(
|
|
9176
|
+
`provider modules regenerated (${providerResult.written.length}) \u2192 ${providerOutputRoot}`
|
|
9177
|
+
);
|
|
9178
|
+
}
|
|
9179
|
+
}
|
|
9180
|
+
if (providerResult.issues.some((i) => i.severity === "error") && !this.continueOnError) {
|
|
9181
|
+
return 1;
|
|
9182
|
+
}
|
|
9183
|
+
} catch (err) {
|
|
9184
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
9185
|
+
if (!isJsonMode()) {
|
|
9186
|
+
printWarning(`provider codegen failed \u2014 ${msg}`);
|
|
9187
|
+
}
|
|
9188
|
+
}
|
|
9189
|
+
try {
|
|
9190
|
+
if (providerResult && !providerResult.skipped && providerResult.issues.length === 0) {
|
|
9191
|
+
const providersDir = providerResult.providersDir;
|
|
9192
|
+
const adapterOutputRoot = path13.resolve(
|
|
9193
|
+
ctx.cwd,
|
|
9194
|
+
backendSrcForHandlers,
|
|
9195
|
+
"integrations"
|
|
9196
|
+
);
|
|
9197
|
+
const entityDefs = fs9.existsSync(entitiesDir) ? loadEntitiesFromYaml(findYamlFiles(entitiesDir)).successes.map(
|
|
9198
|
+
(s) => s.definition
|
|
9199
|
+
) : [];
|
|
9200
|
+
const loadedProviders = loadProvidersFromYaml(
|
|
9201
|
+
findYamlFiles(providersDir)
|
|
9202
|
+
).successes.map((s) => ({
|
|
9203
|
+
definition: s.definition,
|
|
9204
|
+
filePath: s.filePath
|
|
9205
|
+
}));
|
|
9206
|
+
const adapterResult = emitAdapters({
|
|
9207
|
+
providers: loadedProviders,
|
|
9208
|
+
entities: entityDefs,
|
|
9209
|
+
outputRoot: adapterOutputRoot
|
|
9210
|
+
});
|
|
9211
|
+
if (!isJsonMode()) {
|
|
9212
|
+
if (adapterResult.written.length || adapterResult.scaffoldsWritten.length) {
|
|
9213
|
+
printInfo(
|
|
9214
|
+
`adapter codegen: ${adapterResult.scaffoldsWritten.length} scaffold(s) + ${adapterResult.written.length} @generated \u2192 ${adapterOutputRoot}`
|
|
9215
|
+
);
|
|
9216
|
+
}
|
|
9217
|
+
for (const s of adapterResult.scaffoldsSkipped) {
|
|
9218
|
+
printInfo(`skipped scaffold ${s} (author-owned)`);
|
|
9219
|
+
}
|
|
9220
|
+
for (const s of adapterResult.skippedSurfaces) {
|
|
9221
|
+
printWarning(`adapter codegen: ${s.reason} (provider ${s.provider})`);
|
|
9222
|
+
}
|
|
9223
|
+
}
|
|
9224
|
+
}
|
|
9225
|
+
} catch (err) {
|
|
9226
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
9227
|
+
if (!isJsonMode()) {
|
|
9228
|
+
printWarning(`adapter codegen failed \u2014 ${msg}`);
|
|
9229
|
+
}
|
|
9230
|
+
}
|
|
8264
9231
|
if (isJsonMode()) {
|
|
8265
9232
|
printJson({
|
|
8266
9233
|
command: "entity new",
|
|
@@ -10580,7 +11547,7 @@ var REQUIRED_ALIASES = {
|
|
|
10580
11547
|
"@modules/*": ["./src/modules/*"],
|
|
10581
11548
|
"@generated/*": ["./src/generated/*"]
|
|
10582
11549
|
};
|
|
10583
|
-
function
|
|
11550
|
+
function stripJsonComments2(raw) {
|
|
10584
11551
|
let out = "";
|
|
10585
11552
|
let i = 0;
|
|
10586
11553
|
let inString = false;
|
|
@@ -10628,7 +11595,7 @@ var REQUIRED_COMPILER_OPTIONS = {
|
|
|
10628
11595
|
function mergeTsconfig(raw) {
|
|
10629
11596
|
let parsed;
|
|
10630
11597
|
try {
|
|
10631
|
-
parsed = JSON.parse(
|
|
11598
|
+
parsed = JSON.parse(stripJsonComments2(raw));
|
|
10632
11599
|
} catch (err) {
|
|
10633
11600
|
return {
|
|
10634
11601
|
content: raw,
|
|
@@ -11098,8 +12065,8 @@ function ensureMainSwaggerBlock(sourceFile, opts) {
|
|
|
11098
12065
|
/^import\s+\{\s*([^}]+)\s*\}\s+from\s+['"]([^'"]+)['"]\s*;?\s*$/
|
|
11099
12066
|
);
|
|
11100
12067
|
if (match) {
|
|
11101
|
-
const
|
|
11102
|
-
ensureImport(sourceFile, match[2],
|
|
12068
|
+
const names2 = match[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
12069
|
+
ensureImport(sourceFile, match[2], names2);
|
|
11103
12070
|
} else {
|
|
11104
12071
|
sourceFile.insertStatements(0, importLine);
|
|
11105
12072
|
}
|
|
@@ -12105,10 +13072,10 @@ var ProjectInitCommand = class extends Command7 {
|
|
|
12105
13072
|
};
|
|
12106
13073
|
function askConfirm(question) {
|
|
12107
13074
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
12108
|
-
return new Promise((
|
|
13075
|
+
return new Promise((resolve6) => {
|
|
12109
13076
|
rl.question(`${question} [Y/n] `, (answer) => {
|
|
12110
13077
|
rl.close();
|
|
12111
|
-
|
|
13078
|
+
resolve6(answer.trim().toLowerCase() !== "n");
|
|
12112
13079
|
});
|
|
12113
13080
|
});
|
|
12114
13081
|
}
|
|
@@ -13715,7 +14682,7 @@ var junction_default = junctionNoun;
|
|
|
13715
14682
|
// src/cli/commands/events.ts
|
|
13716
14683
|
import fs20 from "fs";
|
|
13717
14684
|
import path32 from "path";
|
|
13718
|
-
import
|
|
14685
|
+
import ts3 from "typescript";
|
|
13719
14686
|
import { Command as Command11, Option as Option11 } from "clipanion";
|
|
13720
14687
|
function scanSourceFileForConsumers(sourceFile, filePath, eventType) {
|
|
13721
14688
|
const tier2 = [];
|
|
@@ -13729,13 +14696,13 @@ function scanSourceFileForConsumers(sourceFile, filePath, eventType) {
|
|
|
13729
14696
|
}
|
|
13730
14697
|
function checkImport(node) {
|
|
13731
14698
|
const moduleSpec = node.moduleSpecifier;
|
|
13732
|
-
if (
|
|
14699
|
+
if (ts3.isStringLiteral(moduleSpec) && moduleSpec.text.includes("subsystems/bridge")) {
|
|
13733
14700
|
hasEventFlowImport = true;
|
|
13734
14701
|
}
|
|
13735
14702
|
const clause = node.importClause;
|
|
13736
14703
|
if (!clause) return;
|
|
13737
14704
|
const named = clause.namedBindings;
|
|
13738
|
-
if (named &&
|
|
14705
|
+
if (named && ts3.isNamedImports(named)) {
|
|
13739
14706
|
for (const el of named.elements) {
|
|
13740
14707
|
const name = el.name.text;
|
|
13741
14708
|
if (name === "EventFlowService" || name === "EVENT_FLOW") {
|
|
@@ -13745,11 +14712,11 @@ function scanSourceFileForConsumers(sourceFile, filePath, eventType) {
|
|
|
13745
14712
|
}
|
|
13746
14713
|
}
|
|
13747
14714
|
function checkCall(node) {
|
|
13748
|
-
if (!
|
|
14715
|
+
if (!ts3.isPropertyAccessExpression(node.expression)) return;
|
|
13749
14716
|
const methodName = node.expression.name.text;
|
|
13750
14717
|
if (methodName !== "publishAndStart" && methodName !== "subscribe") return;
|
|
13751
14718
|
const firstArg = node.arguments[0];
|
|
13752
|
-
if (!firstArg || !
|
|
14719
|
+
if (!firstArg || !ts3.isStringLiteralLike(firstArg)) return;
|
|
13753
14720
|
if (firstArg.text !== eventType) return;
|
|
13754
14721
|
const receiverText = node.expression.expression.getText(sourceFile);
|
|
13755
14722
|
if (methodName === "publishAndStart") {
|
|
@@ -13770,24 +14737,24 @@ function scanSourceFileForConsumers(sourceFile, filePath, eventType) {
|
|
|
13770
14737
|
function findEnclosingClassName(node) {
|
|
13771
14738
|
let cur = node.parent;
|
|
13772
14739
|
while (cur) {
|
|
13773
|
-
if (
|
|
13774
|
-
if (
|
|
14740
|
+
if (ts3.isClassDeclaration(cur) && cur.name) return cur.name.text;
|
|
14741
|
+
if (ts3.isClassExpression(cur) && cur.name) return cur.name.text;
|
|
13775
14742
|
cur = cur.parent;
|
|
13776
14743
|
}
|
|
13777
14744
|
return null;
|
|
13778
14745
|
}
|
|
13779
14746
|
function checkDecoratorOn(member) {
|
|
13780
|
-
const decorators =
|
|
14747
|
+
const decorators = ts3.canHaveDecorators(member) ? ts3.getDecorators(member) ?? [] : [];
|
|
13781
14748
|
for (const decorator of decorators) {
|
|
13782
14749
|
const call = decorator.expression;
|
|
13783
|
-
if (!
|
|
13784
|
-
if (!
|
|
14750
|
+
if (!ts3.isCallExpression(call)) continue;
|
|
14751
|
+
if (!ts3.isIdentifier(call.expression)) continue;
|
|
13785
14752
|
if (call.expression.text !== "OnEvent") continue;
|
|
13786
14753
|
const firstArg = call.arguments[0];
|
|
13787
|
-
if (!firstArg || !
|
|
14754
|
+
if (!firstArg || !ts3.isStringLiteralLike(firstArg)) continue;
|
|
13788
14755
|
if (firstArg.text !== eventType) continue;
|
|
13789
14756
|
const className = findEnclosingClassName(member) ?? "<anonymous>";
|
|
13790
|
-
const memberName =
|
|
14757
|
+
const memberName = ts3.isIdentifier(member.name) ? member.name.text : member.name.getText(sourceFile);
|
|
13791
14758
|
tier1.push({
|
|
13792
14759
|
kind: "on-event",
|
|
13793
14760
|
siteLabel: `${className}.${memberName} @OnEvent('${eventType}')`,
|
|
@@ -13797,12 +14764,12 @@ function scanSourceFileForConsumers(sourceFile, filePath, eventType) {
|
|
|
13797
14764
|
}
|
|
13798
14765
|
}
|
|
13799
14766
|
function visit(node) {
|
|
13800
|
-
if (
|
|
13801
|
-
if (
|
|
13802
|
-
if (
|
|
14767
|
+
if (ts3.isImportDeclaration(node)) checkImport(node);
|
|
14768
|
+
if (ts3.isCallExpression(node)) checkCall(node);
|
|
14769
|
+
if (ts3.isMethodDeclaration(node) || ts3.isPropertyDeclaration(node)) {
|
|
13803
14770
|
checkDecoratorOn(node);
|
|
13804
14771
|
}
|
|
13805
|
-
|
|
14772
|
+
ts3.forEachChild(node, visit);
|
|
13806
14773
|
}
|
|
13807
14774
|
visit(sourceFile);
|
|
13808
14775
|
return { tier2, tier1, hasEventFlowImport };
|
|
@@ -13814,12 +14781,12 @@ function scanDirectoryForConsumers(rootDir, eventType) {
|
|
|
13814
14781
|
let hasEventFlowImport = false;
|
|
13815
14782
|
for (const filePath of files) {
|
|
13816
14783
|
const text2 = fs20.readFileSync(filePath, "utf8");
|
|
13817
|
-
const sourceFile =
|
|
14784
|
+
const sourceFile = ts3.createSourceFile(
|
|
13818
14785
|
filePath,
|
|
13819
14786
|
text2,
|
|
13820
|
-
|
|
14787
|
+
ts3.ScriptTarget.Latest,
|
|
13821
14788
|
true,
|
|
13822
|
-
|
|
14789
|
+
ts3.ScriptKind.TS
|
|
13823
14790
|
);
|
|
13824
14791
|
const result = scanSourceFileForConsumers(sourceFile, filePath, eventType);
|
|
13825
14792
|
tier2.push(...result.tier2);
|
|
@@ -14233,12 +15200,12 @@ async function summary9(ctx) {
|
|
|
14233
15200
|
await reloadRegistry(ctx);
|
|
14234
15201
|
} catch {
|
|
14235
15202
|
}
|
|
14236
|
-
const
|
|
15203
|
+
const names2 = getOrchestrationPatternNames();
|
|
14237
15204
|
return {
|
|
14238
15205
|
title: "orchestration",
|
|
14239
15206
|
body: [
|
|
14240
|
-
`patterns: ${
|
|
14241
|
-
...
|
|
15207
|
+
`patterns: ${names2.length}`,
|
|
15208
|
+
...names2.length > 0 ? [` ${names2.join(", ")}`] : []
|
|
14242
15209
|
]
|
|
14243
15210
|
};
|
|
14244
15211
|
}
|
|
@@ -14287,8 +15254,8 @@ var update_default = UpdateShortcut;
|
|
|
14287
15254
|
// src/cli/index.ts
|
|
14288
15255
|
function readVersion() {
|
|
14289
15256
|
try {
|
|
14290
|
-
const pkgPath =
|
|
14291
|
-
const pkg = JSON.parse(
|
|
15257
|
+
const pkgPath = join13(import.meta.dirname, "..", "..", "package.json");
|
|
15258
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
14292
15259
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
14293
15260
|
} catch {
|
|
14294
15261
|
return "0.0.0";
|