@specverse/engines 6.38.0 → 6.38.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/libs/instance-factories/orms/templates/prisma/schema-generator.js +27 -25
- package/dist/libs/instance-factories/services/templates/prisma/step-conventions.js +6 -6
- package/dist/realize/resolver/index.d.ts.map +1 -1
- package/dist/realize/resolver/index.js +23 -1
- package/dist/realize/resolver/index.js.map +1 -1
- package/libs/instance-factories/orms/templates/prisma/__tests__/schema-generator.test.ts +117 -68
- package/libs/instance-factories/orms/templates/prisma/schema-generator.ts +61 -49
- package/libs/instance-factories/services/templates/prisma/step-conventions.ts +15 -7
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ function generatePrismaSchema(context) {
|
|
|
6
6
|
throw new Error("No models found in context for schema generation");
|
|
7
7
|
}
|
|
8
8
|
for (const model of allModels) {
|
|
9
|
-
|
|
9
|
+
resolveAttributeRelationshipCollisions(model);
|
|
10
10
|
}
|
|
11
11
|
const relationMap = buildRelationMap(allModels);
|
|
12
12
|
const backRefs = buildMissingBackRefs(allModels, relationMap);
|
|
@@ -39,40 +39,42 @@ ${body}
|
|
|
39
39
|
|
|
40
40
|
${enumBlock}${modelSchemas}`;
|
|
41
41
|
}
|
|
42
|
-
function
|
|
42
|
+
function resolveAttributeRelationshipCollisions(model) {
|
|
43
43
|
if (!model) return;
|
|
44
|
-
const
|
|
44
|
+
const wasArray = Array.isArray(model.attributes);
|
|
45
|
+
const attributes = wasArray ? model.attributes : Object.values(model.attributes || {});
|
|
45
46
|
const relationships = Array.isArray(model.relationships) ? model.relationships : Object.values(model.relationships || {});
|
|
46
|
-
const
|
|
47
|
+
const attrNameToObj = /* @__PURE__ */ new Map();
|
|
47
48
|
for (const attr of attributes) {
|
|
48
|
-
if (attr?.name)
|
|
49
|
+
if (attr?.name) attrNameToObj.set(attr.name, attr);
|
|
49
50
|
}
|
|
50
|
-
const
|
|
51
|
+
const renames = [];
|
|
51
52
|
for (const rel of relationships) {
|
|
52
53
|
const defaultName = rel?.type === "hasMany" || rel?.type === "manyToMany" ? pluralize(String(rel?.target || "").toLowerCase()) : String(rel?.target || "").toLowerCase();
|
|
53
54
|
const candidate = rel?.name || defaultName;
|
|
54
|
-
if (candidate
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
if (!candidate) continue;
|
|
56
|
+
const collidingAttr = attrNameToObj.get(candidate);
|
|
57
|
+
if (!collidingAttr) continue;
|
|
58
|
+
let renamed = `${candidate}Data`;
|
|
59
|
+
let suffix = 2;
|
|
60
|
+
while (attrNameToObj.has(renamed)) {
|
|
61
|
+
renamed = `${candidate}Data${suffix++}`;
|
|
60
62
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const lines = [];
|
|
64
|
-
lines.push(
|
|
65
|
-
`Realize error: model "${model.name}" has attribute/relationship name collision(s):`
|
|
63
|
+
console.warn(
|
|
64
|
+
` \u26A0\uFE0F Schema: model "${model.name}" attribute "${candidate}" renamed to "${renamed}" (collides with ${rel.type} relationship to ${rel.target})`
|
|
66
65
|
);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
attrNameToObj.delete(candidate);
|
|
67
|
+
collidingAttr.name = renamed;
|
|
68
|
+
attrNameToObj.set(renamed, collidingAttr);
|
|
69
|
+
renames.push({ from: candidate, to: renamed });
|
|
70
|
+
}
|
|
71
|
+
if (!wasArray && renames.length > 0 && model.attributes) {
|
|
72
|
+
const rebuilt = {};
|
|
73
|
+
for (const [key, val] of Object.entries(model.attributes)) {
|
|
74
|
+
const rename = renames.find((r) => r.from === key);
|
|
75
|
+
rebuilt[rename ? rename.to : key] = val;
|
|
71
76
|
}
|
|
72
|
-
|
|
73
|
-
'Rename one to disambiguate (e.g. "' + collisions[0].name + 'Data" for the attribute, keeping "' + collisions[0].name + '" for the relationship).'
|
|
74
|
-
);
|
|
75
|
-
throw new Error(lines.join("\n"));
|
|
77
|
+
model.attributes = rebuilt;
|
|
76
78
|
}
|
|
77
79
|
}
|
|
78
80
|
function buildRelationMap(allModels) {
|
|
@@ -156,7 +156,7 @@ const STEP_CONVENTIONS = [
|
|
|
156
156
|
generateCall: (m, ctx) => {
|
|
157
157
|
const field = m[1];
|
|
158
158
|
const rawValue = m[2];
|
|
159
|
-
const modelVar = toVar(ctx.
|
|
159
|
+
const modelVar = toVar(ctx.modelName);
|
|
160
160
|
const val = resolveValue(rawValue, ctx);
|
|
161
161
|
return ` // Step ${ctx.stepNum}: Set ${field} to ${rawValue.trim()}
|
|
162
162
|
await prisma.${modelVar}.update({
|
|
@@ -172,7 +172,7 @@ const STEP_CONVENTIONS = [
|
|
|
172
172
|
generateCall: (m, ctx) => {
|
|
173
173
|
const field = m[1];
|
|
174
174
|
const amount = m[2];
|
|
175
|
-
const modelVar = toVar(ctx.
|
|
175
|
+
const modelVar = toVar(ctx.modelName);
|
|
176
176
|
const amountVal = /^\d+$/.test(amount) ? amount : amount;
|
|
177
177
|
return ` // Step ${ctx.stepNum}: Increment ${field} by ${amount}
|
|
178
178
|
await prisma.${modelVar}.update({
|
|
@@ -188,7 +188,7 @@ const STEP_CONVENTIONS = [
|
|
|
188
188
|
generateCall: (m, ctx) => {
|
|
189
189
|
const field = m[1];
|
|
190
190
|
const amount = m[2];
|
|
191
|
-
const modelVar = toVar(ctx.
|
|
191
|
+
const modelVar = toVar(ctx.modelName);
|
|
192
192
|
const amountVal = /^\d+$/.test(amount) ? amount : amount;
|
|
193
193
|
return ` // Step ${ctx.stepNum}: Decrement ${field} by ${amount}
|
|
194
194
|
await prisma.${modelVar}.update({
|
|
@@ -207,7 +207,7 @@ const STEP_CONVENTIONS = [
|
|
|
207
207
|
generateCall: (m, ctx) => {
|
|
208
208
|
const event = m[1];
|
|
209
209
|
return ` // Step ${ctx.stepNum}: Emit ${event} event
|
|
210
|
-
await eventBus.publish('${event}', { ${toVar(ctx.
|
|
210
|
+
await eventBus.publish('${event}', { ${toVar(ctx.modelName)}Id: ${toVar(ctx.modelName)}.id, operation: '${ctx.operationName}', timestamp: new Date().toISOString() });`;
|
|
211
211
|
}
|
|
212
212
|
},
|
|
213
213
|
// --- Send notification ---
|
|
@@ -217,7 +217,7 @@ const STEP_CONVENTIONS = [
|
|
|
217
217
|
generateCall: (m, ctx) => {
|
|
218
218
|
const type = m[1];
|
|
219
219
|
return ` // Step ${ctx.stepNum}: Send ${type} notification
|
|
220
|
-
await eventBus.publish('${type}Notification', { ${toVar(ctx.
|
|
220
|
+
await eventBus.publish('${type}Notification', { ${toVar(ctx.modelName)}Id: ${toVar(ctx.modelName)}.id, operation: '${ctx.operationName}' });`;
|
|
221
221
|
}
|
|
222
222
|
},
|
|
223
223
|
// --- Call service ---
|
|
@@ -240,7 +240,7 @@ const STEP_CONVENTIONS = [
|
|
|
240
240
|
const isModel = /^(updated|created|the)\s+/i.test(value) || value === ctx.modelName;
|
|
241
241
|
if (isModel) {
|
|
242
242
|
return ` // Step ${ctx.stepNum}: Return result
|
|
243
|
-
return ${toVar(ctx.
|
|
243
|
+
return ${toVar(ctx.modelName)};`;
|
|
244
244
|
}
|
|
245
245
|
return ` // Step ${ctx.stepNum}: Return ${value}
|
|
246
246
|
return ${value};`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/realize/resolver/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAMpE;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,MAAM,EAAE,OAAO,GAAG,KAAK,GAAG,oBAAoB,GAAG,kBAAkB,CAAC;IACpE;;;;OAIG;IACH,IAAI,EAAE,WAAW,CAAC;IAClB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;OAEG;IACH,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACjD;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,IAAI,EAAE,YAAY,CAAC;IACnB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IAErG;;;;;;;;OAQG;IACH,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;CACnG;AAMD,qBAAa,yBAA0B,SAAQ,KAAK;aAGhC,IAAI,EAAE,MAAM;aACZ,UAAU,EAAE,MAAM,EAAE;gBAFpC,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAAE;CAKvC;AAMD,+DAA+D;AAC/D,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,aAAa,EAAE,CAAC;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/realize/resolver/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAMpE;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,MAAM,EAAE,OAAO,GAAG,KAAK,GAAG,oBAAoB,GAAG,kBAAkB,CAAC;IACpE;;;;OAIG;IACH,IAAI,EAAE,WAAW,CAAC;IAClB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;OAEG;IACH,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACjD;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,IAAI,EAAE,YAAY,CAAC;IACnB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IAErG;;;;;;;;OAQG;IACH,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;CACnG;AAMD,qBAAa,yBAA0B,SAAQ,KAAK;aAGhC,IAAI,EAAE,MAAM;aACZ,UAAU,EAAE,MAAM,EAAE;gBAFpC,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAAE;CAKvC;AAMD,+DAA+D;AAC/D,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,aAAa,EAAE,CAAC;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAiVD;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,IAAI,kBAAkB,CAE7D"}
|
|
@@ -140,8 +140,30 @@ class DependencyResolverImpl {
|
|
|
140
140
|
const effectiveVersion = version ?? DEFAULT_VERSION;
|
|
141
141
|
const triedSteps = [];
|
|
142
142
|
// ── Step 1: Local component in same spec ────────────────────────────────
|
|
143
|
+
// Components can land as either an array or a name-keyed object
|
|
144
|
+
// depending on which point in the analyse/inference pipeline assembled
|
|
145
|
+
// the spec. Pre-fix this site did `(options.spec?.components ?? []).find`
|
|
146
|
+
// which only handled the array+undefined cases — object-shaped
|
|
147
|
+
// components crashed with `((intermediate value) ?? []).find is not a
|
|
148
|
+
// function` (idle-meta surfaced 7 of these per run).
|
|
149
|
+
//
|
|
150
|
+
// When the spec keys components by name (`components: { Services: {...} }`)
|
|
151
|
+
// the values usually omit `.name` because the key IS the name. Match
|
|
152
|
+
// by key first; fall back to the inner `.name` only for array-shaped
|
|
153
|
+
// components (which always carry their name as a field).
|
|
143
154
|
triedSteps.push('local-component');
|
|
144
|
-
const
|
|
155
|
+
const componentsAny = options.spec?.components;
|
|
156
|
+
let localMatch;
|
|
157
|
+
if (Array.isArray(componentsAny)) {
|
|
158
|
+
localMatch = componentsAny.find((c) => c?.name === from);
|
|
159
|
+
}
|
|
160
|
+
else if (componentsAny && typeof componentsAny === 'object') {
|
|
161
|
+
const keyed = componentsAny;
|
|
162
|
+
const value = keyed[from];
|
|
163
|
+
if (value) {
|
|
164
|
+
localMatch = { name: from, version: value.version };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
145
167
|
if (localMatch) {
|
|
146
168
|
return {
|
|
147
169
|
fromName: from,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/realize/resolver/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/realize/resolver/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* Tests assert substrings (or thrown-error properties) rather than exact
|
|
21
21
|
* file equality so unrelated formatting tweaks don't cascade.
|
|
22
22
|
*/
|
|
23
|
-
import { describe, it, expect } from 'vitest';
|
|
23
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
24
24
|
import generatePrismaSchema from '../schema-generator.js';
|
|
25
25
|
|
|
26
26
|
const simpleModel = {
|
|
@@ -268,7 +268,16 @@ describe('prisma/schema-generator — 53B *At field DateTime promotion', () => {
|
|
|
268
268
|
});
|
|
269
269
|
|
|
270
270
|
describe('prisma/schema-generator — 53C attribute/relationship collision', () => {
|
|
271
|
-
|
|
271
|
+
// Prior behaviour: the first collision threw, killing emission of every
|
|
272
|
+
// subsequent model. New behaviour: rename the colliding attribute to
|
|
273
|
+
// `<name>Data` (the historical error message's own suggestion), emit a
|
|
274
|
+
// console.warn, and proceed so the rest of the spec still produces a
|
|
275
|
+
// Prisma schema. The auto-rename only happens inside the schema
|
|
276
|
+
// generator — downstream services that key off the original attribute
|
|
277
|
+
// name will need spec-level disambiguation if they reference the
|
|
278
|
+
// renamed field; the goal here is "realize produces a usable schema",
|
|
279
|
+
// not "spec is healed end-to-end".
|
|
280
|
+
it('auto-renames the colliding attribute and still emits the model + relationship', () => {
|
|
272
281
|
const saveGameModel = {
|
|
273
282
|
name: 'SaveGame',
|
|
274
283
|
attributes: [
|
|
@@ -284,50 +293,36 @@ describe('prisma/schema-generator — 53C attribute/relationship collision', ()
|
|
|
284
293
|
attributes: [{ name: 'id', type: 'String', required: true }],
|
|
285
294
|
relationships: [],
|
|
286
295
|
};
|
|
287
|
-
|
|
288
|
-
|
|
296
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
297
|
+
let out = '';
|
|
298
|
+
let messages = '';
|
|
299
|
+
try {
|
|
300
|
+
out = generatePrismaSchema({
|
|
289
301
|
spec: { models: [saveGameModel, gameStateModel] },
|
|
290
302
|
models: [saveGameModel, gameStateModel],
|
|
291
|
-
} as any)
|
|
292
|
-
).toThrow(/SaveGame.*attribute.*relationship.*collision/i);
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it('error message references the colliding name AND the relationship type/target', () => {
|
|
296
|
-
const model = {
|
|
297
|
-
name: 'SaveGame',
|
|
298
|
-
attributes: [
|
|
299
|
-
{ name: 'id', type: 'String', required: true },
|
|
300
|
-
{ name: 'gameState', type: 'String' },
|
|
301
|
-
],
|
|
302
|
-
relationships: [
|
|
303
|
-
{ name: 'gameState', type: 'belongsTo', target: 'GameState' },
|
|
304
|
-
],
|
|
305
|
-
};
|
|
306
|
-
const target = {
|
|
307
|
-
name: 'GameState',
|
|
308
|
-
attributes: [{ name: 'id', type: 'String', required: true }],
|
|
309
|
-
relationships: [],
|
|
310
|
-
};
|
|
311
|
-
let err: Error | null = null;
|
|
312
|
-
try {
|
|
313
|
-
generatePrismaSchema({
|
|
314
|
-
spec: { models: [model, target] },
|
|
315
|
-
models: [model, target],
|
|
316
303
|
} as any);
|
|
317
|
-
|
|
318
|
-
|
|
304
|
+
// Capture warn.mock.calls BEFORE mockRestore — restoring resets state.
|
|
305
|
+
messages = warn.mock.calls.map((c) => c.join(' ')).join('\n');
|
|
306
|
+
} finally {
|
|
307
|
+
warn.mockRestore();
|
|
319
308
|
}
|
|
320
|
-
|
|
321
|
-
expect(
|
|
322
|
-
|
|
323
|
-
expect(
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
309
|
+
// Original String attribute is now named gameStateData
|
|
310
|
+
expect(out).toMatch(/gameStateData\s+String/);
|
|
311
|
+
// belongsTo relationship keeps the canonical `gameState` name
|
|
312
|
+
expect(out).toMatch(/gameState\s+GameState\b/);
|
|
313
|
+
// SaveGame still emits, GameState still emits
|
|
314
|
+
expect(out).toMatch(/model SaveGame \{/);
|
|
315
|
+
expect(out).toMatch(/model GameState \{/);
|
|
316
|
+
// Warning emitted naming the model + the rename
|
|
317
|
+
expect(messages).toContain('SaveGame');
|
|
318
|
+
expect(messages).toContain('gameState');
|
|
319
|
+
expect(messages).toContain('gameStateData');
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('renames against default-derived relationship names too (no explicit rel.name)', () => {
|
|
328
323
|
// Default name for a hasMany relation = pluralised target lowercase.
|
|
329
|
-
//
|
|
330
|
-
const
|
|
324
|
+
// The attribute walker can produce a field that shadows that default.
|
|
325
|
+
const userModel = {
|
|
331
326
|
name: 'User',
|
|
332
327
|
attributes: [
|
|
333
328
|
{ name: 'id', type: 'String', required: true },
|
|
@@ -340,22 +335,23 @@ describe('prisma/schema-generator — 53C attribute/relationship collision', ()
|
|
|
340
335
|
};
|
|
341
336
|
const post = {
|
|
342
337
|
name: 'Post',
|
|
343
|
-
attributes: [
|
|
344
|
-
|
|
345
|
-
],
|
|
346
|
-
relationships: [
|
|
347
|
-
{ type: 'belongsTo', target: 'User' },
|
|
348
|
-
],
|
|
338
|
+
attributes: [{ name: 'id', type: 'String', required: true }],
|
|
339
|
+
relationships: [{ type: 'belongsTo', target: 'User' }],
|
|
349
340
|
};
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
341
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
342
|
+
let out: string;
|
|
343
|
+
try {
|
|
344
|
+
out = generatePrismaSchema({
|
|
345
|
+
spec: { models: [userModel, post] },
|
|
346
|
+
models: [userModel, post],
|
|
347
|
+
} as any);
|
|
348
|
+
} finally {
|
|
349
|
+
warn.mockRestore();
|
|
350
|
+
}
|
|
351
|
+
expect(out).toMatch(/postsData\s+String/);
|
|
356
352
|
});
|
|
357
353
|
|
|
358
|
-
it('proceeds normally when no collision exists (no false positives)', () => {
|
|
354
|
+
it('proceeds normally when no collision exists (no false positives, no warnings)', () => {
|
|
359
355
|
const model = {
|
|
360
356
|
name: 'User',
|
|
361
357
|
attributes: [
|
|
@@ -377,15 +373,23 @@ describe('prisma/schema-generator — 53C attribute/relationship collision', ()
|
|
|
377
373
|
{ name: 'author', type: 'belongsTo', target: 'User' },
|
|
378
374
|
],
|
|
379
375
|
};
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
376
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
377
|
+
let callCount = 0;
|
|
378
|
+
try {
|
|
379
|
+
expect(() =>
|
|
380
|
+
generatePrismaSchema({
|
|
381
|
+
spec: { models: [model, post] },
|
|
382
|
+
models: [model, post],
|
|
383
|
+
} as any)
|
|
384
|
+
).not.toThrow();
|
|
385
|
+
callCount = warn.mock.calls.length;
|
|
386
|
+
} finally {
|
|
387
|
+
warn.mockRestore();
|
|
388
|
+
}
|
|
389
|
+
expect(callCount).toBe(0);
|
|
386
390
|
});
|
|
387
391
|
|
|
388
|
-
it('
|
|
392
|
+
it('renames multiple colliding attributes in a single model independently', () => {
|
|
389
393
|
const model = {
|
|
390
394
|
name: 'WideModel',
|
|
391
395
|
attributes: [
|
|
@@ -400,18 +404,63 @@ describe('prisma/schema-generator — 53C attribute/relationship collision', ()
|
|
|
400
404
|
};
|
|
401
405
|
const foo = { name: 'Foo', attributes: [{ name: 'id', type: 'String' }], relationships: [] };
|
|
402
406
|
const bar = { name: 'Bar', attributes: [{ name: 'id', type: 'String' }], relationships: [] };
|
|
403
|
-
|
|
407
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
408
|
+
let out: string;
|
|
404
409
|
try {
|
|
405
|
-
generatePrismaSchema({
|
|
410
|
+
out = generatePrismaSchema({
|
|
406
411
|
spec: { models: [model, foo, bar] },
|
|
407
412
|
models: [model, foo, bar],
|
|
408
413
|
} as any);
|
|
409
|
-
}
|
|
410
|
-
|
|
414
|
+
} finally {
|
|
415
|
+
warn.mockRestore();
|
|
416
|
+
}
|
|
417
|
+
expect(out).toMatch(/fooData\s+String/);
|
|
418
|
+
expect(out).toMatch(/barData\s+String/);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('blast-radius regression: a collision in one model does NOT prevent later models from emitting', () => {
|
|
422
|
+
// Pre-fix this site threw on the first collision, killing emission of
|
|
423
|
+
// every subsequent model in the loop. Pin the new contract: 24 models
|
|
424
|
+
// with one offender still produces all 24 in the output.
|
|
425
|
+
const offender = {
|
|
426
|
+
name: 'SaveGame',
|
|
427
|
+
attributes: [
|
|
428
|
+
{ name: 'id', type: 'String', required: true },
|
|
429
|
+
{ name: 'gameState', type: 'String' },
|
|
430
|
+
],
|
|
431
|
+
relationships: [
|
|
432
|
+
{ name: 'gameState', type: 'belongsTo', target: 'GameState' },
|
|
433
|
+
],
|
|
434
|
+
};
|
|
435
|
+
const innocent1 = {
|
|
436
|
+
name: 'Player',
|
|
437
|
+
attributes: [
|
|
438
|
+
{ name: 'id', type: 'String', required: true },
|
|
439
|
+
{ name: 'name', type: 'String' },
|
|
440
|
+
],
|
|
441
|
+
relationships: [],
|
|
442
|
+
};
|
|
443
|
+
const innocent2 = {
|
|
444
|
+
name: 'GameState',
|
|
445
|
+
attributes: [
|
|
446
|
+
{ name: 'id', type: 'String', required: true },
|
|
447
|
+
{ name: 'phase', type: 'String' },
|
|
448
|
+
],
|
|
449
|
+
relationships: [],
|
|
450
|
+
};
|
|
451
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
452
|
+
let out: string;
|
|
453
|
+
try {
|
|
454
|
+
out = generatePrismaSchema({
|
|
455
|
+
spec: { models: [offender, innocent1, innocent2] },
|
|
456
|
+
models: [offender, innocent1, innocent2],
|
|
457
|
+
} as any);
|
|
458
|
+
} finally {
|
|
459
|
+
warn.mockRestore();
|
|
411
460
|
}
|
|
412
|
-
expect(
|
|
413
|
-
expect(
|
|
414
|
-
expect(
|
|
461
|
+
expect(out).toMatch(/model SaveGame \{/);
|
|
462
|
+
expect(out).toMatch(/model Player \{/);
|
|
463
|
+
expect(out).toMatch(/model GameState \{/);
|
|
415
464
|
});
|
|
416
465
|
});
|
|
417
466
|
|
|
@@ -24,10 +24,16 @@ export default function generatePrismaSchema(context: TemplateContext): string {
|
|
|
24
24
|
// surface from two analyse-pipeline outputs (an attribute walker emits
|
|
25
25
|
// `gameState: String`; a relationship walker emits `gameState: belongsTo
|
|
26
26
|
// GameState`) and silently produce a Prisma file that refuses to compile.
|
|
27
|
-
//
|
|
28
|
-
//
|
|
27
|
+
//
|
|
28
|
+
// History: pre-fix this site threw on the first collision, which killed
|
|
29
|
+
// emission of every subsequent model in the loop — one bad model in a
|
|
30
|
+
// 24-model spec left zero schema on disk. The new behaviour auto-renames
|
|
31
|
+
// the attribute side to `<name>Data` (matching the suggestion in the
|
|
32
|
+
// historical error message) so the relationship keeps its canonical
|
|
33
|
+
// name, all 24 models still emit, and a console.warn surfaces each
|
|
34
|
+
// rename in the realize log so the spec author can clean up upstream.
|
|
29
35
|
for (const model of allModels) {
|
|
30
|
-
|
|
36
|
+
resolveAttributeRelationshipCollisions(model);
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
// Build relation map: track all references to each target model
|
|
@@ -81,74 +87,80 @@ export default function generatePrismaSchema(context: TemplateContext): string {
|
|
|
81
87
|
}
|
|
82
88
|
|
|
83
89
|
/**
|
|
84
|
-
*
|
|
90
|
+
* Resolve duplicate field names within a single model (53C).
|
|
91
|
+
*
|
|
92
|
+
* Prisma rejects models where the same name is declared twice. The
|
|
93
|
+
* common collision pattern from the analyse pipeline is an attribute
|
|
94
|
+
* walker emitting `gameState: String` while a relationship walker
|
|
95
|
+
* emits `gameState: belongsTo GameState` on the same model.
|
|
85
96
|
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* facets of the source (TS interface + Zod schema).
|
|
97
|
+
* Resolution policy: keep the relationship's canonical name (which
|
|
98
|
+
* surfaces in `select`/`include` etc. and is the user-facing handle),
|
|
99
|
+
* and rename the colliding attribute to `<name>Data`. If `<name>Data`
|
|
100
|
+
* also collides, append a numeric suffix until unique. Each rename
|
|
101
|
+
* emits a console.warn so the realize log surfaces what changed.
|
|
92
102
|
*
|
|
93
|
-
* We
|
|
94
|
-
*
|
|
95
|
-
* error message says "field already defined" with no hint about the
|
|
96
|
-
* source). Attribute-vs-attribute duplicates are caught by spec validation
|
|
103
|
+
* We only resolve the relationship-vs-attribute collision here.
|
|
104
|
+
* Attribute-vs-attribute duplicates are caught by spec validation
|
|
97
105
|
* earlier in the pipeline.
|
|
98
106
|
*/
|
|
99
|
-
function
|
|
107
|
+
function resolveAttributeRelationshipCollisions(model: any): void {
|
|
100
108
|
if (!model) return;
|
|
101
|
-
const
|
|
109
|
+
const wasArray = Array.isArray(model.attributes);
|
|
110
|
+
const attributes: any[] = wasArray
|
|
102
111
|
? model.attributes
|
|
103
112
|
: Object.values(model.attributes || {});
|
|
104
113
|
const relationships = Array.isArray(model.relationships)
|
|
105
114
|
? model.relationships
|
|
106
115
|
: Object.values(model.relationships || {});
|
|
107
116
|
|
|
108
|
-
const
|
|
109
|
-
for (const attr of attributes
|
|
110
|
-
if (attr?.name)
|
|
117
|
+
const attrNameToObj = new Map<string, any>();
|
|
118
|
+
for (const attr of attributes) {
|
|
119
|
+
if (attr?.name) attrNameToObj.set(attr.name, attr);
|
|
111
120
|
}
|
|
112
121
|
|
|
113
|
-
const
|
|
122
|
+
const renames: Array<{ from: string; to: string }> = [];
|
|
114
123
|
for (const rel of relationships as any[]) {
|
|
115
|
-
// Relationships emit a field named after `rel.name` (or a derived
|
|
116
|
-
// when name is absent). The default for hasMany/manyToMany
|
|
117
|
-
// pluralised target lowercase; for belongsTo/hasOne it's the
|
|
118
|
-
// lowercase.
|
|
119
|
-
// the implicit naming gets the same protection as one who set `name:`.
|
|
124
|
+
// Relationships emit a field named after `rel.name` (or a derived
|
|
125
|
+
// default when name is absent). The default for hasMany/manyToMany
|
|
126
|
+
// is the pluralised target lowercase; for belongsTo/hasOne it's the
|
|
127
|
+
// bare target lowercase.
|
|
120
128
|
const defaultName =
|
|
121
129
|
rel?.type === 'hasMany' || rel?.type === 'manyToMany'
|
|
122
130
|
? pluralize(String(rel?.target || '').toLowerCase())
|
|
123
131
|
: String(rel?.target || '').toLowerCase();
|
|
124
132
|
const candidate = rel?.name || defaultName;
|
|
125
|
-
if (candidate
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
133
|
+
if (!candidate) continue;
|
|
134
|
+
const collidingAttr = attrNameToObj.get(candidate);
|
|
135
|
+
if (!collidingAttr) continue;
|
|
136
|
+
|
|
137
|
+
let renamed = `${candidate}Data`;
|
|
138
|
+
let suffix = 2;
|
|
139
|
+
while (attrNameToObj.has(renamed)) {
|
|
140
|
+
renamed = `${candidate}Data${suffix++}`;
|
|
131
141
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const lines: string[] = [];
|
|
136
|
-
lines.push(
|
|
137
|
-
`Realize error: model "${model.name}" has attribute/relationship name collision(s):`
|
|
142
|
+
console.warn(
|
|
143
|
+
` ⚠️ Schema: model "${model.name}" attribute "${candidate}" renamed to "${renamed}" ` +
|
|
144
|
+
`(collides with ${rel.type} relationship to ${rel.target})`
|
|
138
145
|
);
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
146
|
+
attrNameToObj.delete(candidate);
|
|
147
|
+
collidingAttr.name = renamed;
|
|
148
|
+
attrNameToObj.set(renamed, collidingAttr);
|
|
149
|
+
renames.push({ from: candidate, to: renamed });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Object-form attributes carry the field name in the object key as well
|
|
153
|
+
// as `.name`. Rebuild the object so downstream emitters that read
|
|
154
|
+
// Object.entries (e.g. controller-generator's validation walker) see the
|
|
155
|
+
// renamed key. Array-form needs no rewrite — the inner `.name` is
|
|
156
|
+
// already the canonical handle.
|
|
157
|
+
if (!wasArray && renames.length > 0 && model.attributes) {
|
|
158
|
+
const rebuilt: Record<string, any> = {};
|
|
159
|
+
for (const [key, val] of Object.entries(model.attributes)) {
|
|
160
|
+
const rename = renames.find((r) => r.from === key);
|
|
161
|
+
rebuilt[rename ? rename.to : key] = val;
|
|
143
162
|
}
|
|
144
|
-
|
|
145
|
-
'Rename one to disambiguate (e.g. "' +
|
|
146
|
-
collisions[0].name +
|
|
147
|
-
'Data" for the attribute, keeping "' +
|
|
148
|
-
collisions[0].name +
|
|
149
|
-
'" for the relationship).'
|
|
150
|
-
);
|
|
151
|
-
throw new Error(lines.join('\n'));
|
|
163
|
+
model.attributes = rebuilt;
|
|
152
164
|
}
|
|
153
165
|
}
|
|
154
166
|
|
|
@@ -21,7 +21,15 @@ import {
|
|
|
21
21
|
export type StepConvention = SharedConvention<StepContext>;
|
|
22
22
|
|
|
23
23
|
export interface StepContext extends SharedStepContext {
|
|
24
|
-
|
|
24
|
+
// Optional: legacy callers (ai-behaviors-generator, controller-generator)
|
|
25
|
+
// supply this as a pre-lowercased model var. The per-action emitter
|
|
26
|
+
// (engines/src/realize/per-action-emitter.ts) builds a plain
|
|
27
|
+
// SharedStepContext and doesn't set it. Conventions below read
|
|
28
|
+
// ctx.modelName (canonical) and call toVar() locally — same result
|
|
29
|
+
// either way, but the per-action path no longer crashes with
|
|
30
|
+
// `Cannot read properties of undefined (reading 'charAt')` when a
|
|
31
|
+
// convention reads ctx.prismaModel that was never supplied.
|
|
32
|
+
prismaModel?: string;
|
|
25
33
|
}
|
|
26
34
|
|
|
27
35
|
/**
|
|
@@ -229,7 +237,7 @@ export const STEP_CONVENTIONS: StepConvention[] = [
|
|
|
229
237
|
generateCall: (m, ctx) => {
|
|
230
238
|
const field = m[1];
|
|
231
239
|
const rawValue = m[2];
|
|
232
|
-
const modelVar = toVar(ctx.
|
|
240
|
+
const modelVar = toVar(ctx.modelName);
|
|
233
241
|
const val = resolveValue(rawValue, ctx);
|
|
234
242
|
return ` // Step ${ctx.stepNum}: Set ${field} to ${rawValue.trim()}
|
|
235
243
|
await prisma.${modelVar}.update({
|
|
@@ -246,7 +254,7 @@ export const STEP_CONVENTIONS: StepConvention[] = [
|
|
|
246
254
|
generateCall: (m, ctx) => {
|
|
247
255
|
const field = m[1];
|
|
248
256
|
const amount = m[2];
|
|
249
|
-
const modelVar = toVar(ctx.
|
|
257
|
+
const modelVar = toVar(ctx.modelName);
|
|
250
258
|
const amountVal = /^\d+$/.test(amount) ? amount : amount;
|
|
251
259
|
return ` // Step ${ctx.stepNum}: Increment ${field} by ${amount}
|
|
252
260
|
await prisma.${modelVar}.update({
|
|
@@ -263,7 +271,7 @@ export const STEP_CONVENTIONS: StepConvention[] = [
|
|
|
263
271
|
generateCall: (m, ctx) => {
|
|
264
272
|
const field = m[1];
|
|
265
273
|
const amount = m[2];
|
|
266
|
-
const modelVar = toVar(ctx.
|
|
274
|
+
const modelVar = toVar(ctx.modelName);
|
|
267
275
|
const amountVal = /^\d+$/.test(amount) ? amount : amount;
|
|
268
276
|
return ` // Step ${ctx.stepNum}: Decrement ${field} by ${amount}
|
|
269
277
|
await prisma.${modelVar}.update({
|
|
@@ -284,7 +292,7 @@ export const STEP_CONVENTIONS: StepConvention[] = [
|
|
|
284
292
|
generateCall: (m, ctx) => {
|
|
285
293
|
const event = m[1];
|
|
286
294
|
return ` // Step ${ctx.stepNum}: Emit ${event} event
|
|
287
|
-
await eventBus.publish('${event}', { ${toVar(ctx.
|
|
295
|
+
await eventBus.publish('${event}', { ${toVar(ctx.modelName)}Id: ${toVar(ctx.modelName)}.id, operation: '${ctx.operationName}', timestamp: new Date().toISOString() });`;
|
|
288
296
|
},
|
|
289
297
|
},
|
|
290
298
|
|
|
@@ -295,7 +303,7 @@ export const STEP_CONVENTIONS: StepConvention[] = [
|
|
|
295
303
|
generateCall: (m, ctx) => {
|
|
296
304
|
const type = m[1];
|
|
297
305
|
return ` // Step ${ctx.stepNum}: Send ${type} notification
|
|
298
|
-
await eventBus.publish('${type}Notification', { ${toVar(ctx.
|
|
306
|
+
await eventBus.publish('${type}Notification', { ${toVar(ctx.modelName)}Id: ${toVar(ctx.modelName)}.id, operation: '${ctx.operationName}' });`;
|
|
299
307
|
},
|
|
300
308
|
},
|
|
301
309
|
|
|
@@ -320,7 +328,7 @@ export const STEP_CONVENTIONS: StepConvention[] = [
|
|
|
320
328
|
const isModel = /^(updated|created|the)\s+/i.test(value) || value === ctx.modelName;
|
|
321
329
|
if (isModel) {
|
|
322
330
|
return ` // Step ${ctx.stepNum}: Return result
|
|
323
|
-
return ${toVar(ctx.
|
|
331
|
+
return ${toVar(ctx.modelName)};`;
|
|
324
332
|
}
|
|
325
333
|
return ` // Step ${ctx.stepNum}: Return ${value}
|
|
326
334
|
return ${value};`;
|