@jskit-ai/crud-server-generator 0.1.54 → 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.
@@ -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.54",
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.45",
155
- "@jskit-ai/crud-core": "0.1.54",
156
- "@jskit-ai/database-runtime": "0.1.46",
157
- "@jskit-ai/http-runtime": "0.1.45",
158
- "@jskit-ai/kernel": "0.1.46",
159
- "@jskit-ai/realtime": "0.1.45",
160
- "@jskit-ai/users-core": "0.1.56",
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.54",
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.54",
17
- "@jskit-ai/database-runtime": "0.1.46",
18
- "@jskit-ai/http-runtime": "0.1.45",
19
- "@jskit-ai/kernel": "0.1.46",
20
- "@jskit-ai/users-core": "0.1.56",
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 dbColumnEntries = [];
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
- dbColumnEntries.push({
1170
+ repositoryEntries.push({
1171
1171
  key,
1172
- dbColumn: name
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(dbColumnEntries, relationEntries, enumEntries);
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 dbColumn = normalizeText(entry.dbColumn);
1245
- if (dbColumn) {
1246
- topLevelProperties.push(`dbColumn: ${JSON.stringify(dbColumn)}`);
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 dbColumn = normalizeText(entry?.dbColumn);
488
- if (dbColumn) {
489
- lines.push(` dbColumn: ${JSON.stringify(dbColumn)},`);
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;
@@ -136,7 +136,7 @@ const deleteOutputValidator = Object.freeze({
136
136
  const CRUD_RESOURCE_FIELD_META = [];
137
137
 
138
138
  const crudResource = {
139
- resource: "crud",
139
+ namespace: "crud",
140
140
  tableName: "crud",
141
141
  idColumn: "id",
142
142
  messages: {
@@ -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
- createCrudLookupProviderResolver,
5
- createCrudLookupProvider
6
- } from "@jskit-ai/crud-core/server/lookupProviders";
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
- resolveLookupProvider: createCrudLookupProviderResolver(scope)
43
+ resolveLookup: createCrudLookupResolver(scope)
44
44
  });
45
45
  });
46
46
 
47
- app.singleton("crud.lookup.${option:namespace|snake}", (scope) => {
48
- return createCrudLookupProvider(scope.make("repository.${option:namespace|snake}"), {
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 repositoryRuntime = createCrudRepositoryRuntime(resource, {
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 withTransaction = createWithTransaction(knex);
11
+ const resourceRuntime = createCrudResourceRuntime(resource, knex, {
12
+ ...options,
13
+ ...REPOSITORY_CONFIG
14
+ });
21
15
 
22
16
  async function list(query = {}, callOptions = {}) {
23
- return crudRepositoryList(repositoryRuntime, knex, query, options, callOptions);
17
+ return resourceRuntime.list(query, callOptions);
24
18
  }
25
19
 
26
20
  async function findById(recordId, callOptions = {}) {
27
- return crudRepositoryFindById(repositoryRuntime, knex, recordId, options, callOptions);
21
+ return resourceRuntime.findById(recordId, callOptions);
28
22
  }
29
23
 
30
24
  async function listByIds(ids = [], callOptions = {}) {
31
- return crudRepositoryListByIds(repositoryRuntime, knex, ids, options, callOptions);
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 crudRepositoryCreate(repositoryRuntime, knex, payload, options, callOptions);
33
+ return resourceRuntime.create(payload, callOptions);
36
34
  }
37
35
 
38
36
  async function updateById(recordId, patch = {}, callOptions = {}) {
39
- return crudRepositoryUpdateById(repositoryRuntime, knex, recordId, patch, options, callOptions);
37
+ return resourceRuntime.updateById(recordId, patch, callOptions);
40
38
  }
41
39
 
42
40
  async function deleteById(recordId, callOptions = {}) {
43
- return crudRepositoryDeleteById(repositoryRuntime, knex, recordId, options, callOptions);
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
- resource: "${option:namespace|snake}",
86
+ namespace: "${option:namespace|snake}",
87
87
  tableName: __JSKIT_CRUD_TABLE_NAME__,
88
88
  idColumn: __JSKIT_CRUD_ID_COLUMN__,
89
89
  messages: {
@@ -70,7 +70,7 @@ const patchBodyValidator = Object.freeze({
70
70
  const RESOURCE_FIELD_META = [];
71
71
 
72
72
  const resource = {
73
- resource: "contacts",
73
+ namespace: "contacts",
74
74
  tableName: "contacts",
75
75
  idColumn: "id",
76
76
  operations: {
@@ -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\/repositoryMethods";/
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 repositoryRuntime = createCrudRepositoryRuntime\(/);
834
- assert.match(templateSource, /return crudRepositoryList\(repositoryRuntime, knex, query, options, callOptions\);/);
835
- assert.match(templateSource, /return crudRepositoryFindById\(repositoryRuntime, knex, recordId, options, callOptions\);/);
836
- assert.match(templateSource, /return crudRepositoryListByIds\(repositoryRuntime, knex, ids, options, callOptions\);/);
837
- assert.match(templateSource, /return crudRepositoryCreate\(repositoryRuntime, knex, payload, options, callOptions\);/);
838
- assert.match(templateSource, /return crudRepositoryUpdateById\(repositoryRuntime, knex, recordId, patch, options, callOptions\);/);
839
- assert.match(templateSource, /return crudRepositoryDeleteById\(repositoryRuntime, knex, recordId, options, callOptions\);/);
840
- assert.doesNotMatch(templateSource, /listByForeignIds/);
841
- assert.doesNotMatch(templateSource, /crudRepository requires knex/);
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\/lookupProviders";/
970
+ /from "@jskit-ai\/crud-core\/server\/lookups";/
965
971
  );
966
- assert.match(templateSource, /resolveLookupProvider: createCrudLookupProviderResolver\(scope\)/);
972
+ assert.match(templateSource, /resolveLookup: createCrudLookupResolver\(scope\)/);
967
973
  assert.match(
968
974
  templateSource,
969
- /return createCrudLookupProvider\(scope\.make\("repository\.\$\{option:namespace\|snake\}"\), \{\s*ownershipFilter: crudPolicy\.ownershipFilter\s*\}\);/
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
  });
@@ -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 patch normalization via the shared CRUD service", async () => {
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
 
@@ -185,7 +185,7 @@ const patchBodyValidator = Object.freeze({
185
185
  });
186
186
 
187
187
  const resource = Object.freeze({
188
- resource: "customers",
188
+ namespace: "customers",
189
189
  tableName: "customers",
190
190
  idColumn: "id",
191
191
  operations: {