@ghentcdh/crouton-api 0.0.1-alpha.44 → 0.0.1-alpha.46

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/index.cjs CHANGED
@@ -40,7 +40,7 @@ __export(index_exports, {
40
40
  ResourceConfigLoader: () => ResourceConfigLoader2,
41
41
  ResourceConfigRegistry: () => ResourceConfigRegistry,
42
42
  ResourceHooksSchema: () => ResourceHooksSchema,
43
- SecurityGuardRegistry: () => SecurityGuardRegistry2,
43
+ SecurityGuardRegistry: () => SecurityGuardRegistry,
44
44
  WriteOpSchema: () => WriteOpSchema,
45
45
  buildFilterWhere: () => buildFilterWhere,
46
46
  buildViews: () => buildViews,
@@ -666,6 +666,48 @@ var JsonOperationsSchema = import_zod9.z.object({
666
666
  upsert: BoolOrUpsertSchema.default(false),
667
667
  delete: OpEntry.default(true)
668
668
  });
669
+ var buildSubResourceOperations = /* @__PURE__ */ __name((ops, baseUri, idField = "id") => {
670
+ if (!ops) return {};
671
+ const idPlaceholder = `{${idField}}`;
672
+ return {
673
+ ...ops.findAll && {
674
+ findAll: {
675
+ uri: baseUri,
676
+ method: "get"
677
+ }
678
+ },
679
+ ...ops.findOne && {
680
+ findOne: {
681
+ uri: `${baseUri}/${idPlaceholder}`,
682
+ method: "get"
683
+ }
684
+ },
685
+ ...ops.create && {
686
+ create: {
687
+ uri: baseUri,
688
+ method: "post"
689
+ }
690
+ },
691
+ ...ops.update && {
692
+ update: {
693
+ uri: `${baseUri}/${idPlaceholder}`,
694
+ method: "put"
695
+ }
696
+ },
697
+ ...ops.patch && {
698
+ patch: {
699
+ uri: `${baseUri}/${idPlaceholder}`,
700
+ method: "patch"
701
+ }
702
+ },
703
+ ...ops.delete && {
704
+ delete: {
705
+ uri: `${baseUri}/${idPlaceholder}`,
706
+ method: "delete"
707
+ }
708
+ }
709
+ };
710
+ }, "buildSubResourceOperations");
669
711
 
670
712
  // ../crouton-core/src/lib/resource/ResourceJson.schema.ts
671
713
  var import_zod19 = require("zod");
@@ -2187,11 +2229,21 @@ var ViewColumnConfigSchema = import_zod23.z.object({
2187
2229
  fieldInput: FieldInputSchema.optional()
2188
2230
  });
2189
2231
  var ViewConfigSchema = import_zod23.z.object({
2190
- json_schema: import_zod23.z.record(import_zod23.z.string(), import_zod23.z.unknown()),
2191
- ui_schema: import_zod23.z.record(import_zod23.z.string(), import_zod23.z.unknown()),
2232
+ // TODO why we have both?
2233
+ json_schema: import_zod23.z.record(import_zod23.z.string(), import_zod23.z.unknown()).optional(),
2234
+ ui_schema: import_zod23.z.record(import_zod23.z.string(), import_zod23.z.unknown()).optional(),
2235
+ // TODO why we have both?
2236
+ ui: import_zod23.z.record(import_zod23.z.string(), import_zod23.z.unknown()).optional(),
2237
+ data: import_zod23.z.record(import_zod23.z.string(), import_zod23.z.unknown()).optional(),
2192
2238
  columns: import_zod23.z.array(ViewColumnConfigSchema),
2193
2239
  defaultSort: import_zod23.z.string().optional()
2194
2240
  });
2241
+ var viewDefList = [
2242
+ "table",
2243
+ "view",
2244
+ "form"
2245
+ ];
2246
+ var ViewDefEnum = import_zod23.z.enum(viewDefList);
2195
2247
 
2196
2248
  // ../crouton-core/src/lib/i18n/Translations.schema.ts
2197
2249
  var import_zod24 = require("zod");
@@ -2297,7 +2349,7 @@ var resolveLanguage = /* @__PURE__ */ __name((acceptLanguage, supported, default
2297
2349
 
2298
2350
  // src/lib/crouton-api.module.ts
2299
2351
  var import_common24 = require("@nestjs/common");
2300
- var import_core = require("@nestjs/core");
2352
+ var import_core2 = require("@nestjs/core");
2301
2353
 
2302
2354
  // src/lib/crud/app-layout/app-layout.types.ts
2303
2355
  var import_zod25 = require("zod");
@@ -2573,6 +2625,12 @@ var resolveColumnFieldVariants = /* @__PURE__ */ __name((cols) => cols?.map((col
2573
2625
 
2574
2626
  // src/lib/crud/translation/localize-resource.ts
2575
2627
  var clone = /* @__PURE__ */ __name((obj) => JSON.parse(JSON.stringify(obj)), "clone");
2628
+ var cloneResource = /* @__PURE__ */ __name((config) => {
2629
+ const { definition, ...rest } = config;
2630
+ const localized = clone(rest);
2631
+ if (definition) localized.definition = definition;
2632
+ return localized;
2633
+ }, "cloneResource");
2576
2634
  var translateEnumOptions = /* @__PURE__ */ __name((columns, t) => {
2577
2635
  if (!columns) return;
2578
2636
  for (const col of columns) {
@@ -2622,7 +2680,7 @@ var patchViewTitles = /* @__PURE__ */ __name((views, columnLabels) => {
2622
2680
  }, "patchViewTitles");
2623
2681
  var localizeResource = /* @__PURE__ */ __name((config, t) => {
2624
2682
  const name = config.name;
2625
- const localized = clone(config);
2683
+ const localized = cloneResource(config);
2626
2684
  localized.title = t(resourceTitleKey(name), config.title ?? config.tag);
2627
2685
  if (localized.sidebar) {
2628
2686
  const sidebarTranslation = t(resourceSidebarKey(name), config.sidebar?.label);
@@ -4878,6 +4936,10 @@ var import_common12 = require("@nestjs/common");
4878
4936
  var import_swagger2 = require("@nestjs/swagger");
4879
4937
 
4880
4938
  // src/lib/crud/security/crouton-security.guard.ts
4939
+ var import_common11 = require("@nestjs/common");
4940
+ var import_core = require("@nestjs/core");
4941
+
4942
+ // src/lib/crud/security/security-guard.registry.ts
4881
4943
  var import_common10 = require("@nestjs/common");
4882
4944
  function _ts_decorate6(decorators, target, key, desc2) {
4883
4945
  var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
@@ -4899,48 +4961,32 @@ function _ts_metadata6(metadataKey, metadataValue) {
4899
4961
  }
4900
4962
  }
4901
4963
  __name(_ts_metadata6, "_ts_metadata");
4902
- var CROUTON_SECURITY = "crouton:security";
4903
- var CroutonSecurityGuard = class {
4964
+ var SecurityGuardRegistry = class {
4904
4965
  static {
4905
- __name(this, "CroutonSecurityGuard");
4966
+ __name(this, "SecurityGuardRegistry");
4906
4967
  }
4907
- reflector;
4908
- registry;
4909
- moduleRef;
4910
- constructor(reflector, registry, moduleRef) {
4911
- this.reflector = reflector;
4912
- this.registry = registry;
4913
- this.moduleRef = moduleRef;
4968
+ guards;
4969
+ constructor(guards = {}) {
4970
+ this.guards = guards;
4914
4971
  }
4915
- async canActivate(context) {
4916
- const sec = this.reflector.get(CROUTON_SECURITY, context.getHandler());
4917
- if (!sec || "public" in sec) return true;
4918
- const names = Array.isArray(sec.guard) ? sec.guard : [
4919
- sec.guard
4920
- ];
4921
- for (const name of names) {
4922
- const cls = this.registry.resolve(name);
4923
- const guard = this.moduleRef.get(cls, {
4924
- strict: false
4925
- });
4926
- const ok = await guard.canActivate(context);
4927
- if (!ok) return false;
4928
- }
4929
- return true;
4972
+ resolve(name) {
4973
+ const g = this.guards[name];
4974
+ if (!g) throw new Error(`Unknown security guard "${name}". Register it in security.guards.`);
4975
+ return g;
4976
+ }
4977
+ classes() {
4978
+ return Object.values(this.guards);
4930
4979
  }
4931
4980
  };
4932
- CroutonSecurityGuard = _ts_decorate6([
4981
+ SecurityGuardRegistry = _ts_decorate6([
4933
4982
  (0, import_common10.Injectable)(),
4934
4983
  _ts_metadata6("design:type", Function),
4935
4984
  _ts_metadata6("design:paramtypes", [
4936
- typeof Reflector === "undefined" ? Object : Reflector,
4937
- typeof SecurityGuardRegistry === "undefined" ? Object : SecurityGuardRegistry,
4938
- typeof ModuleRef === "undefined" ? Object : ModuleRef
4985
+ typeof Record === "undefined" ? Object : Record
4939
4986
  ])
4940
- ], CroutonSecurityGuard);
4987
+ ], SecurityGuardRegistry);
4941
4988
 
4942
- // src/lib/crud/security/security-guard.registry.ts
4943
- var import_common11 = require("@nestjs/common");
4989
+ // src/lib/crud/security/crouton-security.guard.ts
4944
4990
  function _ts_decorate7(decorators, target, key, desc2) {
4945
4991
  var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
4946
4992
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -4961,30 +5007,45 @@ function _ts_metadata7(metadataKey, metadataValue) {
4961
5007
  }
4962
5008
  }
4963
5009
  __name(_ts_metadata7, "_ts_metadata");
4964
- var SecurityGuardRegistry2 = class {
5010
+ var CROUTON_SECURITY = "crouton:security";
5011
+ var CroutonSecurityGuard = class {
4965
5012
  static {
4966
- __name(this, "SecurityGuardRegistry");
4967
- }
4968
- guards;
4969
- constructor(guards = {}) {
4970
- this.guards = guards;
5013
+ __name(this, "CroutonSecurityGuard");
4971
5014
  }
4972
- resolve(name) {
4973
- const g = this.guards[name];
4974
- if (!g) throw new Error(`Unknown security guard "${name}". Register it in security.guards.`);
4975
- return g;
5015
+ reflector;
5016
+ registry;
5017
+ moduleRef;
5018
+ constructor(reflector, registry, moduleRef) {
5019
+ this.reflector = reflector;
5020
+ this.registry = registry;
5021
+ this.moduleRef = moduleRef;
4976
5022
  }
4977
- classes() {
4978
- return Object.values(this.guards);
5023
+ async canActivate(context) {
5024
+ const sec = this.reflector.get(CROUTON_SECURITY, context.getHandler());
5025
+ if (!sec || "public" in sec) return true;
5026
+ const names = Array.isArray(sec.guard) ? sec.guard : [
5027
+ sec.guard
5028
+ ];
5029
+ for (const name of names) {
5030
+ const cls = this.registry.resolve(name);
5031
+ const guard = this.moduleRef.get(cls, {
5032
+ strict: false
5033
+ });
5034
+ const ok = await guard.canActivate(context);
5035
+ if (!ok) return false;
5036
+ }
5037
+ return true;
4979
5038
  }
4980
5039
  };
4981
- SecurityGuardRegistry2 = _ts_decorate7([
5040
+ CroutonSecurityGuard = _ts_decorate7([
4982
5041
  (0, import_common11.Injectable)(),
4983
5042
  _ts_metadata7("design:type", Function),
4984
5043
  _ts_metadata7("design:paramtypes", [
4985
- typeof Record === "undefined" ? Object : Record
5044
+ typeof import_core.Reflector === "undefined" ? Object : import_core.Reflector,
5045
+ typeof SecurityGuardRegistry === "undefined" ? Object : SecurityGuardRegistry,
5046
+ typeof import_core.ModuleRef === "undefined" ? Object : import_core.ModuleRef
4986
5047
  ])
4987
- ], SecurityGuardRegistry2);
5048
+ ], CroutonSecurityGuard);
4988
5049
 
4989
5050
  // src/lib/crud/operations/decorator.utils.ts
4990
5051
  var def = /* @__PURE__ */ __name((cls, method, fn) => {
@@ -5596,48 +5657,6 @@ var writeRawResourceJson = /* @__PURE__ */ __name((jsonPath, raw) => {
5596
5657
 
5597
5658
  // src/lib/crud/operations/payload-builders.ts
5598
5659
  var resolveEnvPlaceholders = /* @__PURE__ */ __name((value) => value.replace(/\{env\.([^}]+)\}/g, (match, varName) => process.env[varName] ?? match), "resolveEnvPlaceholders");
5599
- var buildSubResourceOperations = /* @__PURE__ */ __name((ops, baseUri, idField = "id") => {
5600
- if (!ops) return {};
5601
- const idPlaceholder = `{${idField}}`;
5602
- return {
5603
- ...ops.findAll && {
5604
- findAll: {
5605
- uri: baseUri,
5606
- method: "get"
5607
- }
5608
- },
5609
- ...ops.findOne && {
5610
- findOne: {
5611
- uri: `${baseUri}/${idPlaceholder}`,
5612
- method: "get"
5613
- }
5614
- },
5615
- ...ops.create && {
5616
- create: {
5617
- uri: baseUri,
5618
- method: "post"
5619
- }
5620
- },
5621
- ...ops.update && {
5622
- update: {
5623
- uri: `${baseUri}/${idPlaceholder}`,
5624
- method: "put"
5625
- }
5626
- },
5627
- ...ops.patch && {
5628
- patch: {
5629
- uri: `${baseUri}/${idPlaceholder}`,
5630
- method: "patch"
5631
- }
5632
- },
5633
- ...ops.delete && {
5634
- delete: {
5635
- uri: `${baseUri}/${idPlaceholder}`,
5636
- method: "delete"
5637
- }
5638
- }
5639
- };
5640
- }, "buildSubResourceOperations");
5641
5660
  var RESOURCE_OPS = [
5642
5661
  "findAll",
5643
5662
  "findOne",
@@ -6177,7 +6196,7 @@ var registerEndpoint = /* @__PURE__ */ __name((ctx, sub) => {
6177
6196
  }, "registerEndpoint");
6178
6197
 
6179
6198
  // src/lib/crud/crud-controller.factory.ts
6180
- function createCrudController(config, baseUrl, moduleDefaultSecurity) {
6199
+ function createCrudController(config, baseUrl, moduleDefaultSecurity, securityEnabled = false) {
6181
6200
  const { route, name, tag, idType = "string" } = config;
6182
6201
  const definition = resolveDefinition(config);
6183
6202
  const listSchema = schemaFor(definition, "findAll");
@@ -6242,7 +6261,9 @@ function createCrudController(config, baseUrl, moduleDefaultSecurity) {
6242
6261
  };
6243
6262
  registerEndpoints(ctx);
6244
6263
  (0, import_common21.Controller)(resourceControllerPath(route, config.parent))(CrudControllerBase);
6245
- (0, import_common21.UseGuards)(CroutonSecurityGuard)(CrudControllerBase);
6264
+ if (securityEnabled) {
6265
+ (0, import_common21.UseGuards)(CroutonSecurityGuard)(CrudControllerBase);
6266
+ }
6246
6267
  (0, import_swagger11.ApiTags)(tag)(CrudControllerBase);
6247
6268
  Object.defineProperty(CrudControllerBase, "name", {
6248
6269
  value: `${name.charAt(0).toUpperCase() + name.slice(1)}Controller`
@@ -7907,6 +7928,35 @@ var upsertOp = /* @__PURE__ */ __name((entry, schema) => {
7907
7928
  }
7908
7929
  return void 0;
7909
7930
  }, "upsertOp");
7931
+ var buildResourceDefinitions = /* @__PURE__ */ __name((schema, operations, enrichedColumns) => {
7932
+ const picked = pickByColumns(schema, enrichedColumns);
7933
+ const createSchema = pickByColumns(schema, enrichedColumns, (c) => !c.idField && c.createable !== false);
7934
+ const updateSchema = pickByColumns(schema, enrichedColumns, (c) => !c.idField && c.updateable !== false);
7935
+ const definition = {
7936
+ ...opWithSchema(operations.findAll, picked) && {
7937
+ findAll: opWithSchema(operations.findAll, picked)
7938
+ },
7939
+ ...opWithSchema(operations.findOne, picked) && {
7940
+ findOne: opWithSchema(operations.findOne, picked)
7941
+ },
7942
+ ...opWithSchema(operations.create, createSchema) && {
7943
+ create: opWithSchema(operations.create, createSchema)
7944
+ },
7945
+ ...opWithSchema(operations.update, updateSchema) && {
7946
+ update: opWithSchema(operations.update, updateSchema)
7947
+ },
7948
+ ...upsertOp(operations.upsert, createSchema) && {
7949
+ upsert: upsertOp(operations.upsert, createSchema)
7950
+ },
7951
+ ...operations.patch !== false && {
7952
+ patch: true
7953
+ },
7954
+ ...operations.delete !== false && {
7955
+ delete: true
7956
+ }
7957
+ };
7958
+ return definition;
7959
+ }, "buildResourceDefinitions");
7910
7960
 
7911
7961
  // src/lib/crud/adapter/relation-type.ts
7912
7962
  var import_zod33 = require("zod");
@@ -8164,9 +8214,6 @@ var fromJson = /* @__PURE__ */ __name((json, schema, hooks, dirPath, baseUrl, ac
8164
8214
  const subResources = isCustom ? [] : buildSubResources(columns, json.route, json.model ?? "", dirPath, enums, baseUrl);
8165
8215
  const enrichedColumns = resolveColumnFieldVariants(enrichResourceRefColumns(enrichActionColumns(columns, json.route, subResources, baseUrl), dirPath, baseUrl) ?? columns);
8166
8216
  const calculatedColumns = json.calculatedColumns ?? [];
8167
- const picked = pickByColumns(schema, enrichedColumns);
8168
- const createSchema = pickByColumns(schema, enrichedColumns, (c) => !c.idField && c.createable !== false);
8169
- const updateSchema = pickByColumns(schema, enrichedColumns, (c) => !c.idField && c.updateable !== false);
8170
8217
  let views = isCustom ? buildViewsFromColumnTypes(enrichedColumns) : buildViews(schema, enrichedColumns);
8171
8218
  if (views && calculatedColumns.length) {
8172
8219
  views = {
@@ -8182,29 +8229,7 @@ var fromJson = /* @__PURE__ */ __name((json, schema, hooks, dirPath, baseUrl, ac
8182
8229
  }
8183
8230
  const lookup = buildLookup(enrichedColumns);
8184
8231
  const enrichedInclude = enrichIncludeWithSort(json.include, enrichedColumns);
8185
- const definition = {
8186
- ...opWithSchema(json.operations.findAll, picked) && {
8187
- findAll: opWithSchema(json.operations.findAll, picked)
8188
- },
8189
- ...opWithSchema(json.operations.findOne, picked) && {
8190
- findOne: opWithSchema(json.operations.findOne, picked)
8191
- },
8192
- ...opWithSchema(json.operations.create, createSchema) && {
8193
- create: opWithSchema(json.operations.create, createSchema)
8194
- },
8195
- ...opWithSchema(json.operations.update, updateSchema) && {
8196
- update: opWithSchema(json.operations.update, updateSchema)
8197
- },
8198
- ...upsertOp(json.operations.upsert, createSchema) && {
8199
- upsert: upsertOp(json.operations.upsert, createSchema)
8200
- },
8201
- ...json.operations.patch !== false && {
8202
- patch: true
8203
- },
8204
- ...json.operations.delete !== false && {
8205
- delete: true
8206
- }
8207
- };
8232
+ const definition = buildResourceDefinitions(schema, json.operations, enrichedColumns);
8208
8233
  return {
8209
8234
  ...json,
8210
8235
  name: json.name,
@@ -8826,7 +8851,7 @@ var CroutonApiModule = class _CroutonApiModule {
8826
8851
  static forResources(configs, dataSources, loader, appConfig, config) {
8827
8852
  const { baseUrl, security } = appConfig;
8828
8853
  const dataSourceRegistry = new DataSourceRegistry(dataSources);
8829
- const guardRegistry = new SecurityGuardRegistry2(security?.guards ?? {});
8854
+ const guardRegistry = new SecurityGuardRegistry(security?.guards ?? {});
8830
8855
  const moduleDefaultSecurity = security?.default;
8831
8856
  const startDir = typeof __dirname !== "undefined" ? __dirname : (0, import_node_path20.dirname)((0, import_node_url2.fileURLToPath)(importMetaUrl));
8832
8857
  const enumRegistry = loadEnumRegistry(startDir, config.enumsFile);
@@ -8885,7 +8910,7 @@ var CroutonApiModule = class _CroutonApiModule {
8885
8910
  configRegistry.setTranslationRegistry(translationRegistry);
8886
8911
  }
8887
8912
  const controllers = [
8888
- ...validConfigs.map((c) => createCrudController(c, baseUrl, moduleDefaultSecurity)),
8913
+ ...validConfigs.map((c) => createCrudController(c, baseUrl, moduleDefaultSecurity, !!security)),
8889
8914
  createAppLayoutController(configs, config.sidebarGroups, config.title, config.autoSave ?? true, translationRegistry, config.i18n),
8890
8915
  // Only registered (and thus only visible in Swagger/routing) when the
8891
8916
  // visual resource builder is enabled — see dev-resources.controller.ts.
@@ -8899,7 +8924,7 @@ var CroutonApiModule = class _CroutonApiModule {
8899
8924
  controllers,
8900
8925
  providers: [
8901
8926
  {
8902
- provide: import_core.APP_FILTER,
8927
+ provide: import_core2.APP_FILTER,
8903
8928
  useClass: CroutonValidationExceptionFilter
8904
8929
  },
8905
8930
  {
@@ -8910,19 +8935,23 @@ var CroutonApiModule = class _CroutonApiModule {
8910
8935
  provide: ResourceConfigRegistry,
8911
8936
  useValue: configRegistry
8912
8937
  },
8913
- {
8914
- provide: SecurityGuardRegistry2,
8915
- useValue: guardRegistry
8916
- },
8917
- CroutonSecurityGuard,
8918
- ...guardRegistry.classes(),
8938
+ // Security providers only registered when security is configured;
8939
+ // without guards the dispatching guard is never applied (see factory).
8940
+ ...security ? [
8941
+ {
8942
+ provide: SecurityGuardRegistry,
8943
+ useValue: guardRegistry
8944
+ },
8945
+ CroutonSecurityGuard,
8946
+ ...guardRegistry.classes()
8947
+ ] : [],
8919
8948
  ...translationRegistry ? [
8920
8949
  {
8921
8950
  provide: TranslationRegistry2,
8922
8951
  useValue: translationRegistry
8923
8952
  },
8924
8953
  {
8925
- provide: import_core.APP_INTERCEPTOR,
8954
+ provide: import_core2.APP_INTERCEPTOR,
8926
8955
  useFactory: /* @__PURE__ */ __name(() => new LanguageInterceptor(translationRegistry), "useFactory")
8927
8956
  }
8928
8957
  ] : []