@apexkit/cli 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.js +80 -12
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -141,7 +141,7 @@ const TRIGGER_DEFINITIONS = [
|
|
|
141
141
|
{ id: "graphql", name: "graphql (Custom Resolver)", desc: "Dynamic Query/Mutation field resolver for GraphQL schema." }
|
|
142
142
|
];
|
|
143
143
|
|
|
144
|
-
// --- 4. WORKSPACE TYPES & CONFIG GENERATOR ---
|
|
144
|
+
// --- 4. ACCURATE WORKSPACE TYPES & CONFIG GENERATOR ---
|
|
145
145
|
async function generateWorkspaceFiles() {
|
|
146
146
|
console.log(" ⏳ Fetching collection schemas from DB...");
|
|
147
147
|
let collectionsData = [];
|
|
@@ -155,6 +155,34 @@ async function generateWorkspaceFiles() {
|
|
|
155
155
|
console.warn(` ⚠️ Could not connect to DB for schemas: ${err.message}`);
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
// Pre-calculate lookup maps for collection IDs and Names
|
|
159
|
+
const colIdToName = new Map();
|
|
160
|
+
const colNameToId = new Map();
|
|
161
|
+
collectionsData.forEach((col) => {
|
|
162
|
+
colIdToName.set(String(col.id), col.name);
|
|
163
|
+
colNameToId.set(col.name, col.id);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Calculate Reverse Relations: Target Collection Name -> Array of Reverse Link Definitions
|
|
167
|
+
const reverseRelationsMap = new Map();
|
|
168
|
+
collectionsData.forEach((originCol) => {
|
|
169
|
+
if (originCol.schema?.relations) {
|
|
170
|
+
for (const [relName, relDef] of Object.entries(originCol.schema.relations)) {
|
|
171
|
+
const rawTarget = relDef.target_collection;
|
|
172
|
+
const targetName = colIdToName.get(String(rawTarget)) || rawTarget;
|
|
173
|
+
|
|
174
|
+
if (!reverseRelationsMap.has(targetName)) {
|
|
175
|
+
reverseRelationsMap.set(targetName, []);
|
|
176
|
+
}
|
|
177
|
+
reverseRelationsMap.get(targetName).push({
|
|
178
|
+
originCollection: originCol.name,
|
|
179
|
+
relationFieldName: relName,
|
|
180
|
+
cardinality: relDef.relation_type, // 'many' or 'one'
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
158
186
|
let collectionTypesStr = `export interface Collections {\n`;
|
|
159
187
|
let collectionExpandsStr = `export interface CollectionExpands {\n`;
|
|
160
188
|
const collectionNames = [];
|
|
@@ -165,6 +193,7 @@ async function generateWorkspaceFiles() {
|
|
|
165
193
|
let expandsStr = "";
|
|
166
194
|
|
|
167
195
|
if (col.schema) {
|
|
196
|
+
// 1. STANDARD FIELDS
|
|
168
197
|
if (col.schema.fields) {
|
|
169
198
|
for (const [name, def] of Object.entries(col.schema.fields)) {
|
|
170
199
|
let tsType = "any";
|
|
@@ -183,6 +212,7 @@ async function generateWorkspaceFiles() {
|
|
|
183
212
|
break;
|
|
184
213
|
case "owner":
|
|
185
214
|
case "relation":
|
|
215
|
+
// ✅ Foreign keys and owners are ALWAYS scalar IDs on the record itself
|
|
186
216
|
tsType = "number | string";
|
|
187
217
|
break;
|
|
188
218
|
case "boolean":
|
|
@@ -206,21 +236,59 @@ async function generateWorkspaceFiles() {
|
|
|
206
236
|
}
|
|
207
237
|
const safeName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : `"${name}"`;
|
|
208
238
|
fieldsStr += ` ${safeName}${def.required ? "" : "?"}: ${tsType};\n`;
|
|
239
|
+
|
|
240
|
+
// If it's an Owner field, it expands to the User Profile map
|
|
241
|
+
if (def.type === "owner") {
|
|
242
|
+
expandsStr += ` ${safeName}?: { id: number | string; email: string; role: string; metadata?: Record<string, any> };\n`;
|
|
243
|
+
}
|
|
209
244
|
}
|
|
210
245
|
}
|
|
211
246
|
|
|
247
|
+
// 2. FORWARD RELATIONS (Directly on this collection)
|
|
212
248
|
if (col.schema.relations) {
|
|
213
249
|
for (const [name, rel] of Object.entries(col.schema.relations)) {
|
|
214
|
-
const
|
|
215
|
-
const
|
|
250
|
+
const rawTarget = rel.target_collection;
|
|
251
|
+
const targetColName = colIdToName.get(String(rawTarget)) || rawTarget;
|
|
216
252
|
const safeName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : `"${name}"`;
|
|
217
|
-
fieldsStr += ` ${safeName}${rel.required ? "" : "?"}: ${tsType};\n`;
|
|
218
253
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
254
|
+
// ✅ BULLETPROOF RULE 1:
|
|
255
|
+
// A record holding the foreign key field stores a SCALAR foreign key ID, NEVER an array.
|
|
256
|
+
fieldsStr += ` ${safeName}${rel.required ? "" : "?"}: number | string;\n`;
|
|
257
|
+
|
|
258
|
+
// ✅ BULLETPROOF RULE 2:
|
|
259
|
+
// Forward expansion from the foreign key field points to ONE target record.
|
|
260
|
+
// It is ALWAYS a Map/Object, NEVER an array!
|
|
261
|
+
const targetRecordType = `Collections["${targetColName}"]`;
|
|
262
|
+
const expandSingleType = `{ id: number | string; data: ${targetRecordType}; created: string; updated: string; expand?: any }`;
|
|
263
|
+
|
|
264
|
+
expandsStr += ` ${safeName}?: ${expandSingleType};\n`;
|
|
222
265
|
}
|
|
223
266
|
}
|
|
267
|
+
|
|
268
|
+
// 3. REVERSE RELATIONS (Parent expanding children)
|
|
269
|
+
const reverseLinks = reverseRelationsMap.get(col.name) || [];
|
|
270
|
+
const fieldCountByName = new Map();
|
|
271
|
+
reverseLinks.forEach((rev) => {
|
|
272
|
+
fieldCountByName.set(rev.originCollection, (fieldCountByName.get(rev.originCollection) || 0) + 1);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
reverseLinks.forEach((rev) => {
|
|
276
|
+
const isDuplicate = (fieldCountByName.get(rev.originCollection) || 0) > 1;
|
|
277
|
+
const revKey = isDuplicate ? `${rev.originCollection}_via_${rev.relationFieldName}` : rev.originCollection;
|
|
278
|
+
const safeRevKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(revKey) ? revKey : `"${revKey}"`;
|
|
279
|
+
|
|
280
|
+
const originRecordType = `Collections["${rev.originCollection}"]`;
|
|
281
|
+
const childRecordType = `{ id: number | string; data: ${originRecordType}; created: string; updated: string; expand?: any }`;
|
|
282
|
+
|
|
283
|
+
// ✅ BULLETPROOF RULE 3:
|
|
284
|
+
// Only return an Array when reverse-expanded from parent AND cardinality is 'many'.
|
|
285
|
+
// If the reverse relation is unique ('one'), return a single Map/Object.
|
|
286
|
+
if (rev.cardinality === "one") {
|
|
287
|
+
expandsStr += ` ${safeRevKey}?: ${childRecordType};\n`;
|
|
288
|
+
} else {
|
|
289
|
+
expandsStr += ` ${safeRevKey}?: Array<${childRecordType}>;\n`;
|
|
290
|
+
}
|
|
291
|
+
});
|
|
224
292
|
}
|
|
225
293
|
|
|
226
294
|
collectionTypesStr += ` "${col.name}": {\n${fieldsStr} };\n`;
|
|
@@ -484,7 +552,7 @@ declare global {
|
|
|
484
552
|
export {};
|
|
485
553
|
`;
|
|
486
554
|
fs.writeFileSync("apexkit.d.ts", types.trim());
|
|
487
|
-
console.log(" 📄 Generated apexkit.d.ts with
|
|
555
|
+
console.log(" 📄 Generated apexkit.d.ts with Accurate Scaled & Relational Typings");
|
|
488
556
|
|
|
489
557
|
const jsconfig = {
|
|
490
558
|
compilerOptions: {
|
|
@@ -566,7 +634,7 @@ export default async function (context) {
|
|
|
566
634
|
<head>
|
|
567
635
|
<meta charset="UTF-8">
|
|
568
636
|
<title>{{ title }}</title>
|
|
569
|
-
<
|
|
637
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
570
638
|
<script src="/static/js/htmx.js"></script>
|
|
571
639
|
<script src="/static/js/alpine.js" defer></script>
|
|
572
640
|
</head>
|
|
@@ -575,10 +643,10 @@ export default async function (context) {
|
|
|
575
643
|
<h1 class="text-3xl font-bold text-indigo-400">{{ title }}</h1>
|
|
576
644
|
|
|
577
645
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
578
|
-
{% for
|
|
646
|
+
{% for post in posts %}
|
|
579
647
|
<div class="p-4 bg-slate-800 rounded-lg border border-slate-700 shadow">
|
|
580
|
-
<h2 class="text-xl font-semibold">{{
|
|
581
|
-
<p class="text-slate-400 text-sm mt-2">{{
|
|
648
|
+
<h2 class="text-xl font-semibold">{{ post.data.title | default(value="Item #" ~ post.id) }}</h2>
|
|
649
|
+
<p class="text-slate-400 text-sm mt-2">{{ post.created }}</p>
|
|
582
650
|
</div>
|
|
583
651
|
{% endfor %}
|
|
584
652
|
</div>
|