@jskit-ai/crud-server-generator 0.1.53 → 0.1.55
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.descriptor.mjs +8 -8
- package/package.json +6 -6
- package/src/server/buildTemplateContext.js +13 -7
- package/src/server/subcommands/resourceAst.js +5 -3
- package/src/shared/crud/crudResource.js +1 -1
- package/templates/src/local-package/server/CrudProvider.js +6 -6
- package/templates/src/local-package/server/repository.js +18 -19
- package/templates/src/local-package/shared/crudResource.js +1 -1
- package/test/addFieldSubcommand.test.js +1 -1
- package/test/buildTemplateContext.test.js +19 -13
- package/test/crudService.test.js +11 -4
- package/test-support/templateServerFixture.js +1 -1
package/package.descriptor.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default Object.freeze({
|
|
2
2
|
packageVersion: 1,
|
|
3
3
|
packageId: "@jskit-ai/crud-server-generator",
|
|
4
|
-
version: "0.1.
|
|
4
|
+
version: "0.1.55",
|
|
5
5
|
kind: "generator",
|
|
6
6
|
description: "CRUD server generator with routes, actions, and persistence scaffolding.",
|
|
7
7
|
options: {
|
|
@@ -151,13 +151,13 @@ export default Object.freeze({
|
|
|
151
151
|
mutations: {
|
|
152
152
|
dependencies: {
|
|
153
153
|
runtime: {
|
|
154
|
-
"@jskit-ai/auth-core": "0.1.
|
|
155
|
-
"@jskit-ai/crud-core": "0.1.
|
|
156
|
-
"@jskit-ai/database-runtime": "0.1.
|
|
157
|
-
"@jskit-ai/http-runtime": "0.1.
|
|
158
|
-
"@jskit-ai/kernel": "0.1.
|
|
159
|
-
"@jskit-ai/realtime": "0.1.
|
|
160
|
-
"@jskit-ai/users-core": "0.1.
|
|
154
|
+
"@jskit-ai/auth-core": "0.1.46",
|
|
155
|
+
"@jskit-ai/crud-core": "0.1.55",
|
|
156
|
+
"@jskit-ai/database-runtime": "0.1.47",
|
|
157
|
+
"@jskit-ai/http-runtime": "0.1.46",
|
|
158
|
+
"@jskit-ai/kernel": "0.1.47",
|
|
159
|
+
"@jskit-ai/realtime": "0.1.46",
|
|
160
|
+
"@jskit-ai/users-core": "0.1.57",
|
|
161
161
|
"@local/${option:namespace|kebab}": "file:packages/${option:namespace|kebab}",
|
|
162
162
|
"typebox": "^1.0.81"
|
|
163
163
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/crud-server-generator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.55",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@babel/parser": "^7.29.2",
|
|
16
|
-
"@jskit-ai/crud-core": "0.1.
|
|
17
|
-
"@jskit-ai/database-runtime": "0.1.
|
|
18
|
-
"@jskit-ai/http-runtime": "0.1.
|
|
19
|
-
"@jskit-ai/kernel": "0.1.
|
|
20
|
-
"@jskit-ai/users-core": "0.1.
|
|
16
|
+
"@jskit-ai/crud-core": "0.1.55",
|
|
17
|
+
"@jskit-ai/database-runtime": "0.1.47",
|
|
18
|
+
"@jskit-ai/http-runtime": "0.1.46",
|
|
19
|
+
"@jskit-ai/kernel": "0.1.47",
|
|
20
|
+
"@jskit-ai/users-core": "0.1.57",
|
|
21
21
|
"recast": "^0.23.11",
|
|
22
22
|
"typebox": "^1.0.81"
|
|
23
23
|
}
|
|
@@ -1157,7 +1157,7 @@ function buildFieldMetaEntries({ outputColumns = [], writableColumns = [], snaps
|
|
|
1157
1157
|
}
|
|
1158
1158
|
}
|
|
1159
1159
|
|
|
1160
|
-
const
|
|
1160
|
+
const repositoryEntries = [];
|
|
1161
1161
|
for (const column of fieldColumnsByKey.values()) {
|
|
1162
1162
|
const key = normalizeText(column?.key);
|
|
1163
1163
|
const name = normalizeText(column?.name);
|
|
@@ -1167,9 +1167,11 @@ function buildFieldMetaEntries({ outputColumns = [], writableColumns = [], snaps
|
|
|
1167
1167
|
if (toSnakeCase(key) === name) {
|
|
1168
1168
|
continue;
|
|
1169
1169
|
}
|
|
1170
|
-
|
|
1170
|
+
repositoryEntries.push({
|
|
1171
1171
|
key,
|
|
1172
|
-
|
|
1172
|
+
repository: {
|
|
1173
|
+
column: name
|
|
1174
|
+
}
|
|
1173
1175
|
});
|
|
1174
1176
|
}
|
|
1175
1177
|
|
|
@@ -1235,15 +1237,19 @@ function buildFieldMetaEntries({ outputColumns = [], writableColumns = [], snaps
|
|
|
1235
1237
|
});
|
|
1236
1238
|
}
|
|
1237
1239
|
|
|
1238
|
-
return mergeFieldMetaEntries(
|
|
1240
|
+
return mergeFieldMetaEntries(repositoryEntries, relationEntries, enumEntries);
|
|
1239
1241
|
}
|
|
1240
1242
|
|
|
1241
1243
|
function renderFieldMetaEntryLines(entry = {}) {
|
|
1242
1244
|
const lines = ["RESOURCE_FIELD_META.push({"];
|
|
1243
1245
|
const topLevelProperties = [`key: ${JSON.stringify(entry.key)}`];
|
|
1244
|
-
const
|
|
1245
|
-
if (
|
|
1246
|
-
topLevelProperties.push(
|
|
1246
|
+
const repositoryColumn = normalizeText(entry?.repository?.column);
|
|
1247
|
+
if (repositoryColumn) {
|
|
1248
|
+
topLevelProperties.push([
|
|
1249
|
+
"repository: {",
|
|
1250
|
+
` column: ${JSON.stringify(repositoryColumn)}`,
|
|
1251
|
+
"}"
|
|
1252
|
+
].join("\n"));
|
|
1247
1253
|
}
|
|
1248
1254
|
|
|
1249
1255
|
const relation = entry.relation && typeof entry.relation === "object" ? entry.relation : null;
|
|
@@ -484,9 +484,11 @@ function renderResourceFieldMetaPushStatement(entry = {}) {
|
|
|
484
484
|
const lines = ["RESOURCE_FIELD_META.push({"];
|
|
485
485
|
lines.push(` key: ${JSON.stringify(key)},`);
|
|
486
486
|
|
|
487
|
-
const
|
|
488
|
-
if (
|
|
489
|
-
lines.push(
|
|
487
|
+
const repositoryColumn = normalizeText(entry?.repository?.column);
|
|
488
|
+
if (repositoryColumn) {
|
|
489
|
+
lines.push(" repository: {");
|
|
490
|
+
lines.push(` column: ${JSON.stringify(repositoryColumn)}`);
|
|
491
|
+
lines.push(" },");
|
|
490
492
|
}
|
|
491
493
|
|
|
492
494
|
const relation = entry?.relation && typeof entry.relation === "object" ? entry.relation : null;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
|
|
2
2
|
import { resolveCrudSurfacePolicyFromAppConfig } from "@jskit-ai/crud-core/server/crudModuleConfig";
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from "@jskit-ai/crud-core/server/
|
|
4
|
+
createCrudLookupResolver,
|
|
5
|
+
createCrudLookup
|
|
6
|
+
} from "@jskit-ai/crud-core/server/lookups";
|
|
7
7
|
import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
|
|
8
8
|
import { createRepository } from "./repository.js";
|
|
9
9
|
import {
|
|
@@ -40,12 +40,12 @@ class ${option:namespace|pascal}Provider {
|
|
|
40
40
|
app.singleton("repository.${option:namespace|snake}", (scope) => {
|
|
41
41
|
const knex = scope.make("jskit.database.knex");
|
|
42
42
|
return createRepository(knex, {
|
|
43
|
-
|
|
43
|
+
resolveLookup: createCrudLookupResolver(scope)
|
|
44
44
|
});
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
app.singleton("
|
|
48
|
-
return
|
|
47
|
+
app.singleton("lookup.${option:namespace|snake}", (scope) => {
|
|
48
|
+
return createCrudLookup(scope.make("repository.${option:namespace|snake}"), {
|
|
49
49
|
ownershipFilter: crudPolicy.ownershipFilter
|
|
50
50
|
});
|
|
51
51
|
});
|
|
@@ -1,53 +1,52 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createCrudRepositoryRuntime,
|
|
3
|
-
crudRepositoryList,
|
|
4
|
-
crudRepositoryFindById,
|
|
5
|
-
crudRepositoryListByIds,
|
|
6
|
-
crudRepositoryCreate,
|
|
7
|
-
crudRepositoryUpdateById,
|
|
8
|
-
crudRepositoryDeleteById
|
|
9
|
-
} from "@jskit-ai/crud-core/server/repositoryMethods";
|
|
10
|
-
import { createWithTransaction } from "@jskit-ai/database-runtime/shared";
|
|
1
|
+
import { createCrudResourceRuntime } from "@jskit-ai/crud-core/server/resourceRuntime";
|
|
11
2
|
import { resource } from "../shared/${option:namespace|singular|camel}Resource.js";
|
|
12
3
|
import { LIST_CONFIG } from "./listConfig.js";
|
|
13
4
|
|
|
14
|
-
const
|
|
5
|
+
const REPOSITORY_CONFIG = Object.freeze({
|
|
15
6
|
context: "${option:namespace|snake} repository",
|
|
16
7
|
list: LIST_CONFIG
|
|
17
8
|
});
|
|
18
9
|
|
|
19
10
|
function createRepository(knex, options = {}) {
|
|
20
|
-
const
|
|
11
|
+
const resourceRuntime = createCrudResourceRuntime(resource, knex, {
|
|
12
|
+
...options,
|
|
13
|
+
...REPOSITORY_CONFIG
|
|
14
|
+
});
|
|
21
15
|
|
|
22
16
|
async function list(query = {}, callOptions = {}) {
|
|
23
|
-
return
|
|
17
|
+
return resourceRuntime.list(query, callOptions);
|
|
24
18
|
}
|
|
25
19
|
|
|
26
20
|
async function findById(recordId, callOptions = {}) {
|
|
27
|
-
return
|
|
21
|
+
return resourceRuntime.findById(recordId, callOptions);
|
|
28
22
|
}
|
|
29
23
|
|
|
30
24
|
async function listByIds(ids = [], callOptions = {}) {
|
|
31
|
-
return
|
|
25
|
+
return resourceRuntime.listByIds(ids, callOptions);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function listByForeignIds(ids = [], foreignKey = "", callOptions = {}) {
|
|
29
|
+
return resourceRuntime.listByForeignIds(ids, foreignKey, callOptions);
|
|
32
30
|
}
|
|
33
31
|
|
|
34
32
|
async function create(payload = {}, callOptions = {}) {
|
|
35
|
-
return
|
|
33
|
+
return resourceRuntime.create(payload, callOptions);
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
async function updateById(recordId, patch = {}, callOptions = {}) {
|
|
39
|
-
return
|
|
37
|
+
return resourceRuntime.updateById(recordId, patch, callOptions);
|
|
40
38
|
}
|
|
41
39
|
|
|
42
40
|
async function deleteById(recordId, callOptions = {}) {
|
|
43
|
-
return
|
|
41
|
+
return resourceRuntime.deleteById(recordId, callOptions);
|
|
44
42
|
}
|
|
45
43
|
|
|
46
44
|
return Object.freeze({
|
|
47
|
-
withTransaction,
|
|
45
|
+
withTransaction: resourceRuntime.withTransaction,
|
|
48
46
|
list,
|
|
49
47
|
findById,
|
|
50
48
|
listByIds,
|
|
49
|
+
listByForeignIds,
|
|
51
50
|
create,
|
|
52
51
|
updateById,
|
|
53
52
|
deleteById
|
|
@@ -83,7 +83,7 @@ const deleteOutputValidator = Object.freeze({
|
|
|
83
83
|
const RESOURCE_FIELD_META = [];
|
|
84
84
|
|
|
85
85
|
const resource = {
|
|
86
|
-
|
|
86
|
+
namespace: "${option:namespace|snake}",
|
|
87
87
|
tableName: __JSKIT_CRUD_TABLE_NAME__,
|
|
88
88
|
idColumn: __JSKIT_CRUD_ID_COLUMN__,
|
|
89
89
|
messages: {
|
|
@@ -827,18 +827,24 @@ test("crud repository template defines explicit one-line CRUD methods over repos
|
|
|
827
827
|
const templateSource = await readFile(templatePath, "utf8");
|
|
828
828
|
assert.match(
|
|
829
829
|
templateSource,
|
|
830
|
-
/from "@jskit-ai\/crud-core\/server\/
|
|
830
|
+
/from "@jskit-ai\/crud-core\/server\/resourceRuntime";/
|
|
831
831
|
);
|
|
832
832
|
assert.match(templateSource, /import \{ LIST_CONFIG \} from "\.\/listConfig\.js";/);
|
|
833
|
-
assert.match(templateSource, /const
|
|
834
|
-
assert.match(templateSource, /
|
|
835
|
-
assert.match(templateSource,
|
|
836
|
-
assert.match(templateSource, /
|
|
837
|
-
assert.match(templateSource, /return
|
|
838
|
-
assert.match(templateSource, /return
|
|
839
|
-
assert.match(templateSource, /return
|
|
840
|
-
assert.
|
|
841
|
-
assert.
|
|
833
|
+
assert.match(templateSource, /const REPOSITORY_CONFIG = Object\.freeze\(\{/);
|
|
834
|
+
assert.match(templateSource, /const resourceRuntime = createCrudResourceRuntime\(resource, knex, \{/);
|
|
835
|
+
assert.match(templateSource, /\.\.\.options,/);
|
|
836
|
+
assert.match(templateSource, /\.\.\.REPOSITORY_CONFIG/);
|
|
837
|
+
assert.match(templateSource, /return resourceRuntime\.list\(query, callOptions\);/);
|
|
838
|
+
assert.match(templateSource, /return resourceRuntime\.findById\(recordId, callOptions\);/);
|
|
839
|
+
assert.match(templateSource, /return resourceRuntime\.listByIds\(ids, callOptions\);/);
|
|
840
|
+
assert.match(templateSource, /return resourceRuntime\.listByForeignIds\(ids, foreignKey, callOptions\);/);
|
|
841
|
+
assert.match(templateSource, /return resourceRuntime\.create\(payload, callOptions\);/);
|
|
842
|
+
assert.match(templateSource, /return resourceRuntime\.updateById\(recordId, patch, callOptions\);/);
|
|
843
|
+
assert.match(templateSource, /return resourceRuntime\.deleteById\(recordId, callOptions\);/);
|
|
844
|
+
assert.match(templateSource, /async function listByForeignIds\(ids = \[\], foreignKey = "", callOptions = \{\}\) \{/);
|
|
845
|
+
assert.match(templateSource, /withTransaction: resourceRuntime\.withTransaction/);
|
|
846
|
+
assert.match(templateSource, /return Object\.freeze\(\{/);
|
|
847
|
+
assert.doesNotMatch(templateSource, /crudRepositoryList/);
|
|
842
848
|
});
|
|
843
849
|
|
|
844
850
|
test("crud actions and routes templates share LIST_CONFIG for cursor validation", async () => {
|
|
@@ -961,12 +967,12 @@ test("crud provider template uses shared lookup provider helpers instead of inli
|
|
|
961
967
|
|
|
962
968
|
assert.match(
|
|
963
969
|
templateSource,
|
|
964
|
-
/from "@jskit-ai\/crud-core\/server\/
|
|
970
|
+
/from "@jskit-ai\/crud-core\/server\/lookups";/
|
|
965
971
|
);
|
|
966
|
-
assert.match(templateSource, /
|
|
972
|
+
assert.match(templateSource, /resolveLookup: createCrudLookupResolver\(scope\)/);
|
|
967
973
|
assert.match(
|
|
968
974
|
templateSource,
|
|
969
|
-
/return
|
|
975
|
+
/return createCrudLookup\(scope\.make\("repository\.\$\{option:namespace\|snake\}"\), \{\s*ownershipFilter: crudPolicy\.ownershipFilter\s*\}\);/
|
|
970
976
|
);
|
|
971
977
|
assert.doesNotMatch(templateSource, /normalizePathname\(relation\.apiPath\)/);
|
|
972
978
|
});
|
package/test/crudService.test.js
CHANGED
|
@@ -96,7 +96,7 @@ test("crudService exports default realtime events for create/update/delete", ()
|
|
|
96
96
|
assert.equal(serviceEvents.deleteRecord[0].realtime.event, "customers.record.changed");
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
-
test("crudService passes existing records into
|
|
99
|
+
test("crudService passes existing records into repository update options via the shared CRUD service", async () => {
|
|
100
100
|
const calls = [];
|
|
101
101
|
const service = createService({
|
|
102
102
|
customersRepository: {
|
|
@@ -115,8 +115,8 @@ test("crudService passes existing records into patch normalization via the share
|
|
|
115
115
|
async create(payload) {
|
|
116
116
|
return { id: 1, ...payload };
|
|
117
117
|
},
|
|
118
|
-
async updateById(recordId, payload) {
|
|
119
|
-
calls.push(["updateById", recordId, payload]);
|
|
118
|
+
async updateById(recordId, payload, options = {}) {
|
|
119
|
+
calls.push(["updateById", recordId, payload, options]);
|
|
120
120
|
return {
|
|
121
121
|
id: recordId,
|
|
122
122
|
textField: payload.textField || "",
|
|
@@ -134,7 +134,14 @@ test("crudService passes existing records into patch normalization via the share
|
|
|
134
134
|
|
|
135
135
|
assert.deepEqual(calls, [
|
|
136
136
|
["findById", 4],
|
|
137
|
-
["updateById", 4, { textField: "Changed" }
|
|
137
|
+
["updateById", 4, { textField: "Changed" }, {
|
|
138
|
+
existingRecord: {
|
|
139
|
+
id: 4,
|
|
140
|
+
textField: "Existing",
|
|
141
|
+
dateField: "2026-03-11T00:00:00.000Z",
|
|
142
|
+
numberField: 3
|
|
143
|
+
}
|
|
144
|
+
}]
|
|
138
145
|
]);
|
|
139
146
|
});
|
|
140
147
|
|