@classytic/arc 1.0.0 → 1.0.8

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.
Files changed (36) hide show
  1. package/README.md +65 -35
  2. package/bin/arc.js +118 -103
  3. package/dist/BaseController-nNRS3vpA.d.ts +233 -0
  4. package/dist/adapters/index.d.ts +2 -2
  5. package/dist/{arcCorePlugin-DTPWXcZN.d.ts → arcCorePlugin-CAjBQtZB.d.ts} +1 -1
  6. package/dist/auth/index.d.ts +1 -1
  7. package/dist/cli/commands/generate.d.ts +16 -0
  8. package/dist/cli/commands/generate.js +334 -0
  9. package/dist/cli/commands/init.d.ts +24 -0
  10. package/dist/cli/commands/init.js +2425 -0
  11. package/dist/cli/index.d.ts +4 -43
  12. package/dist/cli/index.js +3160 -411
  13. package/dist/core/index.d.ts +220 -0
  14. package/dist/core/index.js +2764 -0
  15. package/dist/{createApp-pzUAkzbz.d.ts → createApp-CjN9zZSL.d.ts} +1 -1
  16. package/dist/docs/index.js +19 -11
  17. package/dist/factory/index.d.ts +4 -4
  18. package/dist/factory/index.js +6 -23
  19. package/dist/hooks/index.d.ts +1 -1
  20. package/dist/{index-DkAW8BXh.d.ts → index-D5QTob1X.d.ts} +32 -12
  21. package/dist/index.d.ts +7 -203
  22. package/dist/index.js +108 -113
  23. package/dist/org/index.d.ts +1 -1
  24. package/dist/permissions/index.js +5 -2
  25. package/dist/plugins/index.d.ts +2 -2
  26. package/dist/presets/index.d.ts +6 -6
  27. package/dist/presets/index.js +3 -1
  28. package/dist/presets/multiTenant.d.ts +1 -1
  29. package/dist/registry/index.d.ts +2 -2
  30. package/dist/testing/index.d.ts +2 -2
  31. package/dist/testing/index.js +6 -23
  32. package/dist/types/index.d.ts +1 -1
  33. package/dist/{types-0IPhH_NR.d.ts → types-zpN48n6B.d.ts} +1 -1
  34. package/dist/utils/index.d.ts +28 -4
  35. package/dist/utils/index.js +17 -8
  36. package/package.json +8 -14
package/dist/index.js CHANGED
@@ -2286,30 +2286,30 @@ var BaseController = class _BaseController {
2286
2286
  /**
2287
2287
  * Build service context from IRequestContext
2288
2288
  */
2289
- _buildContext(context2) {
2290
- const parsed = this.queryParser.parse(context2.query);
2291
- const arcContext = context2.context;
2292
- const selectString = this._selectToString(parsed.select) ?? context2.query?.select;
2289
+ _buildContext(req) {
2290
+ const parsed = this.queryParser.parse(req.query);
2291
+ const arcContext = req.metadata;
2292
+ const selectString = this._selectToString(parsed.select) ?? req.query?.select;
2293
2293
  const sanitizedSelect = this._sanitizeSelect(selectString, this.schemaOptions);
2294
2294
  return {
2295
- user: context2.user,
2296
- organizationId: arcContext?.organizationId ?? void 0,
2295
+ user: req.user,
2296
+ organizationId: arcContext?.organizationId ?? req.organizationId ?? void 0,
2297
2297
  select: sanitizedSelect ? sanitizedSelect.split(/\s+/) : void 0,
2298
2298
  populate: this._sanitizePopulate(parsed.populate, this.schemaOptions),
2299
- lean: this._parseLean(context2.query?.lean)
2299
+ lean: this._parseLean(req.query?.lean)
2300
2300
  };
2301
2301
  }
2302
2302
  /**
2303
2303
  * Parse query into QueryOptions using queryParser
2304
2304
  */
2305
- _parseQueryOptions(context2) {
2306
- const parsed = this.queryParser.parse(context2.query);
2307
- const arcContext = context2.context;
2305
+ _parseQueryOptions(req) {
2306
+ const parsed = this.queryParser.parse(req.query);
2307
+ const arcContext = req.metadata;
2308
2308
  delete parsed.filters._policyFilters;
2309
2309
  const limit = Math.min(Math.max(1, parsed.limit || this.defaultLimit), this.maxLimit);
2310
2310
  const page = parsed.after ? void 0 : parsed.page ? Math.max(1, parsed.page) : 1;
2311
2311
  const sortString = parsed.sort ? Object.entries(parsed.sort).map(([k, v]) => v === -1 ? `-${k}` : k).join(",") : this.defaultSort;
2312
- const selectString = this._selectToString(parsed.select) ?? context2.query?.select;
2312
+ const selectString = this._selectToString(parsed.select) ?? req.query?.select;
2313
2313
  return {
2314
2314
  page,
2315
2315
  limit,
@@ -2320,23 +2320,24 @@ var BaseController = class _BaseController {
2320
2320
  // MongoKit features
2321
2321
  search: parsed.search,
2322
2322
  after: parsed.after,
2323
- user: context2.user,
2324
- organizationId: arcContext?.organizationId,
2323
+ user: req.user,
2324
+ organizationId: arcContext?.organizationId ?? req.organizationId,
2325
2325
  context: arcContext
2326
2326
  };
2327
2327
  }
2328
2328
  /**
2329
2329
  * Apply org and policy filters
2330
2330
  */
2331
- _applyFilters(options, context2) {
2331
+ _applyFilters(options, req) {
2332
2332
  const filters = { ...options.filters };
2333
- const arcContext = context2.context;
2333
+ const arcContext = req.metadata;
2334
2334
  const policyFilters = arcContext?._policyFilters;
2335
2335
  if (policyFilters) {
2336
2336
  Object.assign(filters, policyFilters);
2337
2337
  }
2338
- if (arcContext?.organizationId) {
2339
- filters.organizationId = arcContext.organizationId;
2338
+ const orgId = arcContext?.organizationId ?? req.organizationId;
2339
+ if (orgId) {
2340
+ filters.organizationId = orgId;
2340
2341
  }
2341
2342
  return { ...options, filters };
2342
2343
  }
@@ -2344,15 +2345,16 @@ var BaseController = class _BaseController {
2344
2345
  * Build filter for single-item operations (get/update/delete)
2345
2346
  * Combines ID filter with policy/org filters for proper security enforcement
2346
2347
  */
2347
- _buildIdFilter(id, context2) {
2348
+ _buildIdFilter(id, req) {
2348
2349
  const filter = { _id: id };
2349
- const arcContext = context2.context;
2350
+ const arcContext = req.metadata;
2350
2351
  const policyFilters = arcContext?._policyFilters;
2351
2352
  if (policyFilters) {
2352
2353
  Object.assign(filter, policyFilters);
2353
2354
  }
2354
- if (arcContext?.organizationId) {
2355
- filter.organizationId = arcContext.organizationId;
2355
+ const orgId = arcContext?.organizationId ?? req.organizationId;
2356
+ if (orgId) {
2357
+ filter.organizationId = orgId;
2356
2358
  }
2357
2359
  return filter;
2358
2360
  }
@@ -2436,8 +2438,8 @@ var BaseController = class _BaseController {
2436
2438
  * Validates that fetched item satisfies all policy constraints
2437
2439
  * Supports MongoDB query operators: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $exists, $regex, $and, $or
2438
2440
  */
2439
- _checkPolicyFilters(item, context2) {
2440
- const arcContext = context2.context;
2441
+ _checkPolicyFilters(item, req) {
2442
+ const arcContext = req.metadata;
2441
2443
  const policyFilters = arcContext?._policyFilters;
2442
2444
  if (!policyFilters) return true;
2443
2445
  if (policyFilters.$and && Array.isArray(policyFilters.$and)) {
@@ -2520,8 +2522,8 @@ var BaseController = class _BaseController {
2520
2522
  return String(itemOrgId) === String(arcContext.organizationId);
2521
2523
  }
2522
2524
  /** Check ownership for update/delete (ownedByUser preset) */
2523
- _checkOwnership(item, context2) {
2524
- const ownershipCheck = context2.context?._ownershipCheck;
2525
+ _checkOwnership(item, req) {
2526
+ const ownershipCheck = req.metadata?._ownershipCheck;
2525
2527
  if (!item || !ownershipCheck) return true;
2526
2528
  const { field, userId } = ownershipCheck;
2527
2529
  const itemOwnerId = item[field];
@@ -2532,8 +2534,8 @@ var BaseController = class _BaseController {
2532
2534
  * Get hook system from context (instance-scoped) or fall back to global singleton
2533
2535
  * This allows proper isolation when running multiple app instances (e.g., in tests)
2534
2536
  */
2535
- _getHooks(context2) {
2536
- const arcMeta = context2.context?.arc;
2537
+ _getHooks(req) {
2538
+ const arcMeta = req.metadata?.arc;
2537
2539
  return arcMeta?.hooks ?? hookSystem;
2538
2540
  }
2539
2541
  // ============================================================================
@@ -2543,9 +2545,9 @@ var BaseController = class _BaseController {
2543
2545
  * List resources with filtering, pagination, sorting
2544
2546
  * Implements IController.list()
2545
2547
  */
2546
- async list(context2) {
2547
- const options = this._parseQueryOptions(context2);
2548
- const filteredOptions = this._applyFilters(options, context2);
2548
+ async list(req) {
2549
+ const options = this._parseQueryOptions(req);
2550
+ const filteredOptions = this._applyFilters(options, req);
2549
2551
  const result = await this.repository.getAll(filteredOptions);
2550
2552
  if (Array.isArray(result)) {
2551
2553
  return {
@@ -2572,8 +2574,8 @@ var BaseController = class _BaseController {
2572
2574
  * Get single resource by ID
2573
2575
  * Implements IController.get()
2574
2576
  */
2575
- async get(context2) {
2576
- const id = context2.params.id;
2577
+ async get(req) {
2578
+ const id = req.params.id;
2577
2579
  if (!id) {
2578
2580
  return {
2579
2581
  success: false,
@@ -2581,11 +2583,11 @@ var BaseController = class _BaseController {
2581
2583
  status: 400
2582
2584
  };
2583
2585
  }
2584
- const options = this._parseQueryOptions(context2);
2585
- const arcContext = context2.context;
2586
+ const options = this._parseQueryOptions(req);
2587
+ const arcContext = req.metadata;
2586
2588
  try {
2587
2589
  const item = await this.repository.getById(id, options);
2588
- if (!item || !this._checkOrgScope(item, arcContext) || !this._checkPolicyFilters(item, context2)) {
2590
+ if (!item || !this._checkOrgScope(item, arcContext) || !this._checkPolicyFilters(item, req)) {
2589
2591
  return {
2590
2592
  success: false,
2591
2593
  error: "Resource not found",
@@ -2612,18 +2614,18 @@ var BaseController = class _BaseController {
2612
2614
  * Create new resource
2613
2615
  * Implements IController.create()
2614
2616
  */
2615
- async create(context2) {
2616
- const data = { ...context2.body };
2617
- const arcContext = context2.context;
2617
+ async create(req) {
2618
+ const data = { ...req.body };
2619
+ const arcContext = req.metadata;
2618
2620
  if (arcContext?.organizationId) {
2619
2621
  data.organizationId = arcContext.organizationId;
2620
2622
  }
2621
- const userId = getUserId(context2.user);
2623
+ const userId = getUserId(req.user);
2622
2624
  if (userId) {
2623
2625
  data.createdBy = userId;
2624
2626
  }
2625
- const hooks = this._getHooks(context2);
2626
- const user = context2.user;
2627
+ const hooks = this._getHooks(req);
2628
+ const user = req.user;
2627
2629
  let processedData = data;
2628
2630
  if (this.resourceName) {
2629
2631
  processedData = await hooks.executeBefore(this.resourceName, "create", data, {
@@ -2652,8 +2654,8 @@ var BaseController = class _BaseController {
2652
2654
  * Update existing resource
2653
2655
  * Implements IController.update()
2654
2656
  */
2655
- async update(context2) {
2656
- const id = context2.params.id;
2657
+ async update(req) {
2658
+ const id = req.params.id;
2657
2659
  if (!id) {
2658
2660
  return {
2659
2661
  success: false,
@@ -2661,9 +2663,9 @@ var BaseController = class _BaseController {
2661
2663
  status: 400
2662
2664
  };
2663
2665
  }
2664
- const data = { ...context2.body };
2665
- const arcContext = context2.context;
2666
- const user = context2.user;
2666
+ const data = { ...req.body };
2667
+ const arcContext = req.metadata;
2668
+ const user = req.user;
2667
2669
  const userId = getUserId(user);
2668
2670
  if (userId) {
2669
2671
  data.updatedBy = userId;
@@ -2676,14 +2678,14 @@ var BaseController = class _BaseController {
2676
2678
  status: 404
2677
2679
  };
2678
2680
  }
2679
- if (!this._checkOrgScope(existing, arcContext) || !this._checkPolicyFilters(existing, context2)) {
2681
+ if (!this._checkOrgScope(existing, arcContext) || !this._checkPolicyFilters(existing, req)) {
2680
2682
  return {
2681
2683
  success: false,
2682
2684
  error: "Resource not found",
2683
2685
  status: 404
2684
2686
  };
2685
2687
  }
2686
- if (!this._checkOwnership(existing, context2)) {
2688
+ if (!this._checkOwnership(existing, req)) {
2687
2689
  return {
2688
2690
  success: false,
2689
2691
  error: "You do not have permission to modify this resource",
@@ -2691,7 +2693,7 @@ var BaseController = class _BaseController {
2691
2693
  status: 403
2692
2694
  };
2693
2695
  }
2694
- const hooks = this._getHooks(context2);
2696
+ const hooks = this._getHooks(req);
2695
2697
  let processedData = data;
2696
2698
  if (this.resourceName) {
2697
2699
  processedData = await hooks.executeBefore(this.resourceName, "update", data, {
@@ -2729,8 +2731,8 @@ var BaseController = class _BaseController {
2729
2731
  * Delete resource
2730
2732
  * Implements IController.delete()
2731
2733
  */
2732
- async delete(context2) {
2733
- const id = context2.params.id;
2734
+ async delete(req) {
2735
+ const id = req.params.id;
2734
2736
  if (!id) {
2735
2737
  return {
2736
2738
  success: false,
@@ -2738,8 +2740,8 @@ var BaseController = class _BaseController {
2738
2740
  status: 400
2739
2741
  };
2740
2742
  }
2741
- const arcContext = context2.context;
2742
- const user = context2.user;
2743
+ const arcContext = req.metadata;
2744
+ const user = req.user;
2743
2745
  const existing = await this.repository.getById(id);
2744
2746
  if (!existing) {
2745
2747
  return {
@@ -2748,14 +2750,14 @@ var BaseController = class _BaseController {
2748
2750
  status: 404
2749
2751
  };
2750
2752
  }
2751
- if (!this._checkOrgScope(existing, arcContext) || !this._checkPolicyFilters(existing, context2)) {
2753
+ if (!this._checkOrgScope(existing, arcContext) || !this._checkPolicyFilters(existing, req)) {
2752
2754
  return {
2753
2755
  success: false,
2754
2756
  error: "Resource not found",
2755
2757
  status: 404
2756
2758
  };
2757
2759
  }
2758
- if (!this._checkOwnership(existing, context2)) {
2760
+ if (!this._checkOwnership(existing, req)) {
2759
2761
  return {
2760
2762
  success: false,
2761
2763
  error: "You do not have permission to delete this resource",
@@ -2763,7 +2765,7 @@ var BaseController = class _BaseController {
2763
2765
  status: 403
2764
2766
  };
2765
2767
  }
2766
- const hooks = this._getHooks(context2);
2768
+ const hooks = this._getHooks(req);
2767
2769
  if (this.resourceName) {
2768
2770
  await hooks.executeBefore(this.resourceName, "delete", existing, {
2769
2771
  user,
@@ -2800,8 +2802,9 @@ var BaseController = class _BaseController {
2800
2802
  // Preset Methods (framework-agnostic versions)
2801
2803
  // ============================================================================
2802
2804
  /** Get resource by slug (slugLookup preset) */
2803
- async getBySlug(context2) {
2804
- if (!this.repository.getBySlug) {
2805
+ async getBySlug(req) {
2806
+ const repo = this.repository;
2807
+ if (!repo.getBySlug) {
2805
2808
  return {
2806
2809
  success: false,
2807
2810
  error: "Slug lookup not implemented",
@@ -2809,10 +2812,10 @@ var BaseController = class _BaseController {
2809
2812
  };
2810
2813
  }
2811
2814
  const slugField = this._presetFields.slugField ?? "slug";
2812
- const slug = context2.params[slugField] ?? context2.params.slug;
2813
- const options = this._parseQueryOptions(context2);
2814
- const arcContext = context2.context;
2815
- const item = await this.repository.getBySlug(slug, options);
2815
+ const slug = req.params[slugField] ?? req.params.slug;
2816
+ const options = this._parseQueryOptions(req);
2817
+ const arcContext = req.metadata;
2818
+ const item = await repo.getBySlug(slug, options);
2816
2819
  if (!item || !this._checkOrgScope(item, arcContext)) {
2817
2820
  return {
2818
2821
  success: false,
@@ -2827,17 +2830,18 @@ var BaseController = class _BaseController {
2827
2830
  };
2828
2831
  }
2829
2832
  /** Get soft-deleted resources (softDelete preset) */
2830
- async getDeleted(context2) {
2831
- if (!this.repository.getDeleted) {
2833
+ async getDeleted(req) {
2834
+ const repo = this.repository;
2835
+ if (!repo.getDeleted) {
2832
2836
  return {
2833
2837
  success: false,
2834
2838
  error: "Soft delete not implemented",
2835
2839
  status: 501
2836
2840
  };
2837
2841
  }
2838
- const options = this._parseQueryOptions(context2);
2839
- const filteredOptions = this._applyFilters(options, context2);
2840
- const result = await this.repository.getDeleted(filteredOptions);
2842
+ const options = this._parseQueryOptions(req);
2843
+ const filteredOptions = this._applyFilters(options, req);
2844
+ const result = await repo.getDeleted(filteredOptions);
2841
2845
  if (Array.isArray(result)) {
2842
2846
  return {
2843
2847
  success: true,
@@ -2860,15 +2864,16 @@ var BaseController = class _BaseController {
2860
2864
  };
2861
2865
  }
2862
2866
  /** Restore soft-deleted resource (softDelete preset) */
2863
- async restore(context2) {
2864
- if (!this.repository.restore) {
2867
+ async restore(req) {
2868
+ const repo = this.repository;
2869
+ if (!repo.restore) {
2865
2870
  return {
2866
2871
  success: false,
2867
2872
  error: "Restore not implemented",
2868
2873
  status: 501
2869
2874
  };
2870
2875
  }
2871
- const id = context2.params.id;
2876
+ const id = req.params.id;
2872
2877
  if (!id) {
2873
2878
  return {
2874
2879
  success: false,
@@ -2876,7 +2881,7 @@ var BaseController = class _BaseController {
2876
2881
  status: 400
2877
2882
  };
2878
2883
  }
2879
- const item = await this.repository.restore(id);
2884
+ const item = await repo.restore(id);
2880
2885
  if (!item) {
2881
2886
  return {
2882
2887
  success: false,
@@ -2892,17 +2897,18 @@ var BaseController = class _BaseController {
2892
2897
  };
2893
2898
  }
2894
2899
  /** Get hierarchical tree (tree preset) */
2895
- async getTree(context2) {
2896
- if (!this.repository.getTree) {
2900
+ async getTree(req) {
2901
+ const repo = this.repository;
2902
+ if (!repo.getTree) {
2897
2903
  return {
2898
2904
  success: false,
2899
2905
  error: "Tree structure not implemented",
2900
2906
  status: 501
2901
2907
  };
2902
2908
  }
2903
- const options = this._parseQueryOptions(context2);
2904
- const filteredOptions = this._applyFilters(options, context2);
2905
- const tree = await this.repository.getTree(filteredOptions);
2909
+ const options = this._parseQueryOptions(req);
2910
+ const filteredOptions = this._applyFilters(options, req);
2911
+ const tree = await repo.getTree(filteredOptions);
2906
2912
  return {
2907
2913
  success: true,
2908
2914
  data: tree,
@@ -2910,8 +2916,9 @@ var BaseController = class _BaseController {
2910
2916
  };
2911
2917
  }
2912
2918
  /** Get children of parent (tree preset) */
2913
- async getChildren(context2) {
2914
- if (!this.repository.getChildren) {
2919
+ async getChildren(req) {
2920
+ const repo = this.repository;
2921
+ if (!repo.getChildren) {
2915
2922
  return {
2916
2923
  success: false,
2917
2924
  error: "Tree structure not implemented",
@@ -2919,10 +2926,10 @@ var BaseController = class _BaseController {
2919
2926
  };
2920
2927
  }
2921
2928
  const parentField = this._presetFields.parentField ?? "parent";
2922
- const parentId = context2.params[parentField] ?? context2.params.parent ?? context2.params.id;
2923
- const options = this._parseQueryOptions(context2);
2924
- const filteredOptions = this._applyFilters(options, context2);
2925
- const children = await this.repository.getChildren(parentId, filteredOptions);
2929
+ const parentId = req.params[parentField] ?? req.params.parent ?? req.params.id;
2930
+ const options = this._parseQueryOptions(req);
2931
+ const filteredOptions = this._applyFilters(options, req);
2932
+ const children = await repo.getChildren(parentId, filteredOptions);
2926
2933
  return {
2927
2934
  success: true,
2928
2935
  data: children,
@@ -2974,13 +2981,15 @@ function createRequestContext(req) {
2974
2981
  const user = reqWithExtras.user;
2975
2982
  return {
2976
2983
  ...user,
2984
+ // Normalize ID for MongoDB compatibility
2977
2985
  id: String(user._id ?? user.id),
2978
- _id: user._id ?? user.id,
2979
- // Normalize role: handle both string and array formats
2980
- role: Array.isArray(user.roles) ? user.roles[0] : typeof user.roles === "string" ? user.roles : void 0
2986
+ _id: user._id ?? user.id
2987
+ // Preserve original role/roles/permissions as-is
2988
+ // Devs can define their own authorization structure
2981
2989
  };
2982
2990
  })() : void 0,
2983
- context: {
2991
+ organizationId: reqWithExtras.organizationId,
2992
+ metadata: {
2984
2993
  ...reqWithExtras.context,
2985
2994
  // Include Arc metadata for hook execution
2986
2995
  arc: reqWithExtras.arc,
@@ -3028,8 +3037,8 @@ function sendControllerResponse(reply, response, request) {
3028
3037
  }
3029
3038
  function createFastifyHandler(controllerMethod) {
3030
3039
  return async (req, reply) => {
3031
- const context2 = createRequestContext(req);
3032
- const response = await controllerMethod(context2);
3040
+ const requestContext = createRequestContext(req);
3041
+ const response = await controllerMethod(requestContext);
3033
3042
  sendControllerResponse(reply, response, req);
3034
3043
  };
3035
3044
  }
@@ -3297,15 +3306,16 @@ function allowPublic() {
3297
3306
  return check;
3298
3307
  }
3299
3308
  function requireAuth() {
3300
- return (ctx) => {
3309
+ const check = (ctx) => {
3301
3310
  if (!ctx.user) {
3302
3311
  return { granted: false, reason: "Authentication required" };
3303
3312
  }
3304
3313
  return true;
3305
3314
  };
3315
+ return check;
3306
3316
  }
3307
3317
  function requireRoles(roles, options) {
3308
- return (ctx) => {
3318
+ const check = (ctx) => {
3309
3319
  if (!ctx.user) {
3310
3320
  return { granted: false, reason: "Authentication required" };
3311
3321
  }
@@ -3321,6 +3331,8 @@ function requireRoles(roles, options) {
3321
3331
  reason: `Required roles: ${roles.join(", ")}`
3322
3332
  };
3323
3333
  };
3334
+ check._roles = roles;
3335
+ return check;
3324
3336
  }
3325
3337
  function requireOwnership(ownerField = "userId", options) {
3326
3338
  return (ctx) => {
@@ -4545,9 +4557,7 @@ async function loadPlugin(name, logger) {
4545
4557
  const err = error;
4546
4558
  const isModuleNotFound = err.message.includes("Cannot find module") || err.message.includes("Cannot find package") || err.message.includes("MODULE_NOT_FOUND") || err.message.includes("Could not resolve");
4547
4559
  if (isModuleNotFound && OPTIONAL_PLUGINS.has(name)) {
4548
- logger?.warn(
4549
- `ℹ️ Optional plugin '${name}' skipped (${packageName} not installed)`
4550
- );
4560
+ logger?.warn(`ℹ️ Optional plugin '${name}' skipped (${packageName} not installed)`);
4551
4561
  return null;
4552
4562
  }
4553
4563
  if (isModuleNotFound) {
@@ -4586,10 +4596,7 @@ async function createApp(options) {
4586
4596
  });
4587
4597
  if (config.helmet !== false) {
4588
4598
  const helmet = await loadPlugin("helmet");
4589
- await fastify.register(
4590
- helmet,
4591
- config.helmet ?? {}
4592
- );
4599
+ await fastify.register(helmet, config.helmet ?? {});
4593
4600
  fastify.log.info("✅ Helmet (security headers) enabled");
4594
4601
  } else {
4595
4602
  fastify.log.warn("⚠️ Helmet disabled - security headers not applied");
@@ -4609,20 +4616,14 @@ async function createApp(options) {
4609
4616
  }
4610
4617
  if (config.rateLimit !== false) {
4611
4618
  const rateLimit = await loadPlugin("rateLimit");
4612
- await fastify.register(
4613
- rateLimit,
4614
- config.rateLimit ?? { max: 100, timeWindow: "1 minute" }
4615
- );
4619
+ await fastify.register(rateLimit, config.rateLimit ?? { max: 100, timeWindow: "1 minute" });
4616
4620
  fastify.log.info("✅ Rate limiting enabled");
4617
4621
  } else {
4618
4622
  fastify.log.warn("⚠️ Rate limiting disabled");
4619
4623
  }
4620
4624
  if (config.underPressure !== false) {
4621
4625
  const underPressure = await loadPlugin("underPressure");
4622
- await fastify.register(
4623
- underPressure,
4624
- config.underPressure ?? { exposeStatusRoute: true }
4625
- );
4626
+ await fastify.register(underPressure, config.underPressure ?? { exposeStatusRoute: true });
4626
4627
  fastify.log.info("✅ Health monitoring (under-pressure) enabled");
4627
4628
  } else {
4628
4629
  fastify.log.info("ℹ️ Health monitoring disabled");
@@ -4642,10 +4643,7 @@ async function createApp(options) {
4642
4643
  files: 10
4643
4644
  }
4644
4645
  };
4645
- await fastify.register(multipart, {
4646
- ...multipartDefaults,
4647
- ...config.multipart
4648
- });
4646
+ await fastify.register(multipart, { ...multipartDefaults, ...config.multipart });
4649
4647
  fastify.log.info("✅ Multipart (file uploads) enabled");
4650
4648
  }
4651
4649
  }
@@ -4658,10 +4656,7 @@ async function createApp(options) {
4658
4656
  encoding: "utf8",
4659
4657
  runFirst: true
4660
4658
  };
4661
- await fastify.register(rawBody, {
4662
- ...rawBodyDefaults,
4663
- ...config.rawBody
4664
- });
4659
+ await fastify.register(rawBody, { ...rawBodyDefaults, ...config.rawBody });
4665
4660
  fastify.log.info("✅ Raw body parsing enabled");
4666
4661
  }
4667
4662
  }
@@ -1,5 +1,5 @@
1
1
  import { RouteHandlerMethod, FastifyPluginAsync } from 'fastify';
2
- import { l as RequestContext, Z as OrgScopeOptions, z as RouteHandler } from '../index-DkAW8BXh.js';
2
+ import { b as RequestContext, Z as OrgScopeOptions, z as RouteHandler } from '../index-D5QTob1X.js';
3
3
  import { U as UserBase } from '../types-B99TBmFV.js';
4
4
  import 'mongoose';
5
5
 
@@ -5,15 +5,16 @@ function allowPublic() {
5
5
  return check;
6
6
  }
7
7
  function requireAuth() {
8
- return (ctx) => {
8
+ const check = (ctx) => {
9
9
  if (!ctx.user) {
10
10
  return { granted: false, reason: "Authentication required" };
11
11
  }
12
12
  return true;
13
13
  };
14
+ return check;
14
15
  }
15
16
  function requireRoles(roles, options) {
16
- return (ctx) => {
17
+ const check = (ctx) => {
17
18
  if (!ctx.user) {
18
19
  return { granted: false, reason: "Authentication required" };
19
20
  }
@@ -29,6 +30,8 @@ function requireRoles(roles, options) {
29
30
  reason: `Required roles: ${roles.join(", ")}`
30
31
  };
31
32
  };
33
+ check._roles = roles;
34
+ return check;
32
35
  }
33
36
  function requireOwnership(ownerField = "userId", options) {
34
37
  return (ctx) => {
@@ -1,6 +1,6 @@
1
- export { k as ArcCore, A as ArcCorePluginOptions, G as GracefulShutdownOptions, b as HealthCheck, H as HealthOptions, R as RequestIdOptions, T as TracingOptions, f as arcCorePlugin, j as arcCorePluginFn, d as createSpan, e as gracefulShutdownPlugin, g as gracefulShutdownPluginFn, a as healthPlugin, h as healthPluginFn, i as isTracingAvailable, _ as requestIdPlugin, r as requestIdPluginFn, t as traced, c as tracingPlugin } from '../arcCorePlugin-DTPWXcZN.js';
1
+ export { k as ArcCore, A as ArcCorePluginOptions, G as GracefulShutdownOptions, b as HealthCheck, H as HealthOptions, R as RequestIdOptions, T as TracingOptions, f as arcCorePlugin, j as arcCorePluginFn, d as createSpan, e as gracefulShutdownPlugin, g as gracefulShutdownPluginFn, a as healthPlugin, h as healthPluginFn, i as isTracingAvailable, _ as requestIdPlugin, r as requestIdPluginFn, t as traced, c as tracingPlugin } from '../arcCorePlugin-CAjBQtZB.js';
2
2
  import { FastifyInstance, FastifyRequest } from 'fastify';
3
- import '../index-DkAW8BXh.js';
3
+ import '../index-D5QTob1X.js';
4
4
  import 'mongoose';
5
5
  import '../types-B99TBmFV.js';
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { MultiTenantOptions } from './multiTenant.js';
2
2
  export { default as multiTenantPreset } from './multiTenant.js';
3
- import { P as PresetResult, j as IRequestContext, m as IControllerResponse, n as PaginatedResult, d as AnyRecord, o as ResourceConfig } from '../index-DkAW8BXh.js';
3
+ import { f as PresetResult, a as IRequestContext, c as IControllerResponse, P as PaginatedResult, A as AnyRecord, l as ResourceConfig } from '../index-D5QTob1X.js';
4
4
  import 'mongoose';
5
5
  import 'fastify';
6
6
  import '../types-B99TBmFV.js';
@@ -142,12 +142,12 @@ interface ISoftDeleteController<TDoc = unknown> {
142
142
  * Get all soft-deleted items
143
143
  * Called by: GET /deleted
144
144
  */
145
- getDeleted(context: IRequestContext): Promise<IControllerResponse<PaginatedResult<TDoc>>>;
145
+ getDeleted(req: IRequestContext): Promise<IControllerResponse<PaginatedResult<TDoc>>>;
146
146
  /**
147
147
  * Restore a soft-deleted item by ID
148
148
  * Called by: POST /:id/restore
149
149
  */
150
- restore(context: IRequestContext): Promise<IControllerResponse<TDoc>>;
150
+ restore(req: IRequestContext): Promise<IControllerResponse<TDoc>>;
151
151
  }
152
152
  /**
153
153
  * Slug Lookup Preset Interface
@@ -179,7 +179,7 @@ interface ISlugLookupController<TDoc = unknown> {
179
179
  * Get a resource by its slug
180
180
  * Called by: GET /slug/:slug
181
181
  */
182
- getBySlug(context: IRequestContext): Promise<IControllerResponse<TDoc>>;
182
+ getBySlug(req: IRequestContext): Promise<IControllerResponse<TDoc>>;
183
183
  }
184
184
  /**
185
185
  * Tree Preset Interface
@@ -213,12 +213,12 @@ interface ITreeController<TDoc = unknown> {
213
213
  * Get the full hierarchical tree
214
214
  * Called by: GET /tree
215
215
  */
216
- getTree(context: IRequestContext): Promise<IControllerResponse<TDoc[]>>;
216
+ getTree(req: IRequestContext): Promise<IControllerResponse<TDoc[]>>;
217
217
  /**
218
218
  * Get direct children of a parent node
219
219
  * Called by: GET /:parent/children
220
220
  */
221
- getChildren(context: IRequestContext): Promise<IControllerResponse<TDoc[]>>;
221
+ getChildren(req: IRequestContext): Promise<IControllerResponse<TDoc[]>>;
222
222
  }
223
223
  /**
224
224
  * Owned By User Preset
@@ -5,7 +5,7 @@ function allowPublic() {
5
5
  return check;
6
6
  }
7
7
  function requireRoles(roles, options) {
8
- return (ctx) => {
8
+ const check = (ctx) => {
9
9
  if (!ctx.user) {
10
10
  return { granted: false, reason: "Authentication required" };
11
11
  }
@@ -18,6 +18,8 @@ function requireRoles(roles, options) {
18
18
  reason: `Required roles: ${roles.join(", ")}`
19
19
  };
20
20
  };
21
+ check._roles = roles;
22
+ return check;
21
23
  }
22
24
 
23
25
  // src/presets/softDelete.ts
@@ -1,4 +1,4 @@
1
- import { R as RequestWithExtras, C as CrudRouteKey, P as PresetResult } from '../index-DkAW8BXh.js';
1
+ import { d as RequestWithExtras, e as CrudRouteKey, f as PresetResult } from '../index-D5QTob1X.js';
2
2
  import 'mongoose';
3
3
  import 'fastify';
4
4
  import '../types-B99TBmFV.js';
@@ -1,5 +1,5 @@
1
- import { I as IntrospectionPluginOptions } from '../index-DkAW8BXh.js';
2
- export { c as RegisterOptions, b as ResourceRegistry, r as resourceRegistry } from '../index-DkAW8BXh.js';
1
+ import { i as IntrospectionPluginOptions } from '../index-D5QTob1X.js';
2
+ export { k as RegisterOptions, j as ResourceRegistry, r as resourceRegistry } from '../index-D5QTob1X.js';
3
3
  import { FastifyPluginAsync } from 'fastify';
4
4
  import 'mongoose';
5
5
  import '../types-B99TBmFV.js';