@flusys/nestjs-form-builder 2.0.0 → 3.0.0

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/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Form Builder Package Guide
2
2
 
3
3
  > **Package:** `@flusys/nestjs-form-builder`
4
+ > **Version:** 3.0.0
4
5
  > **Type:** Dynamic form management with schema versioning and access control
5
6
 
6
7
  This guide covers the NestJS form builder package - dynamic form creation, submission storage, and multi-tenant support.
@@ -22,6 +23,9 @@ This guide covers the NestJS form builder package - dynamic form creation, submi
22
23
  - [Computed Fields](#computed-fields)
23
24
  - [Response Mode](#response-mode)
24
25
  - [Best Practices](#best-practices)
26
+ - [Swagger Configuration](#swagger-configuration)
27
+ - [Permission Utilities](#permission-utilities)
28
+ - [Controller Security](#controller-security)
25
29
 
26
30
  ---
27
31
 
@@ -227,39 +231,57 @@ export { Form as FormBase } from './form.entity';
227
231
 
228
232
  Main form entity with all form fields:
229
233
 
230
- | Column | Type | Description |
231
- |--------|------|-------------|
232
- | `name` | `varchar(255)` | Form name |
233
- | `description` | `varchar(500)` | Optional description |
234
- | `slug` | `varchar(255)` | URL-friendly identifier (unique) |
235
- | `schema` | `json` | Form schema (sections, fields, settings) |
236
- | `schemaVersion` | `int` | Auto-incremented on schema changes |
237
- | `accessType` | `enum` | `public`, `authenticated`, `action_group` |
238
- | `actionGroups` | `simple-array` | Permission codes for action_group access |
239
- | `isActive` | `boolean` | Form availability |
240
- | `metadata` | `simple-json` | Additional data |
234
+ | Column | Type | Default | Description |
235
+ |--------|------|---------|-------------|
236
+ | `name` | `varchar(255)` | required | Form name |
237
+ | `description` | `varchar(500)` | null | Optional description |
238
+ | `slug` | `varchar(255)` | null | URL-friendly identifier |
239
+ | `schema` | `json` | required | Form schema (sections, fields, settings) |
240
+ | `schemaVersion` | `int` | 1 | Auto-incremented on schema changes |
241
+ | `accessType` | `varchar(50)` | 'AUTHENTICATED' | `public`, `authenticated`, `action_group` |
242
+ | `actionGroups` | `simple-array` | null | Permission codes for action_group access |
243
+ | `isActive` | `boolean` | true | Form availability |
244
+ | `metadata` | `simple-json` | null | Additional data |
245
+
246
+ **Indexes (Form):**
247
+ - `slug` - Unique index
248
+ - `isActive` - Index for filtering active forms
241
249
 
242
250
  ### Form vs FormWithCompany
243
251
 
244
252
  - **Form** - Used when `enableCompanyFeature: false`
245
253
  - **FormWithCompany** - Extends Form, adds `companyId` column for tenant isolation
246
254
 
255
+ **FormWithCompany Additional Column:**
256
+
257
+ | Column | Type | Description |
258
+ |--------|------|-------------|
259
+ | `companyId` | `uuid` | Company ID for tenant isolation |
260
+
261
+ **FormWithCompany Indexes:**
262
+ - `companyId` - Index for company filtering
263
+ - `companyId, slug` - Unique compound index (slugs unique per company)
264
+ - `companyId, isActive` - Compound index for active forms per company
265
+
247
266
  ### FormResult
248
267
 
249
268
  Stores form submissions:
250
269
 
251
- | Column | Type | Description |
252
- |--------|------|-------------|
253
- | `formId` | `uuid` | Reference to form |
254
- | `schemaVersionSnapshot` | `json` | Full schema copy at submission time |
255
- | `schemaVersion` | `int` | Schema version at submission |
256
- | `data` | `json` | Submitted field values |
257
- | `submittedById` | `uuid` | User who submitted (null for public) |
258
- | `submittedAt` | `timestamp` | Submission timestamp |
259
- | `isDraft` | `boolean` | Draft vs final submission |
260
- | `metadata` | `simple-json` | Additional data |
270
+ | Column | Type | Default | Description |
271
+ |--------|------|---------|-------------|
272
+ | `formId` | `uuid` | required | Reference to form |
273
+ | `schemaVersionSnapshot` | `json` | required | Full schema copy at submission time |
274
+ | `schemaVersion` | `int` | required | Schema version at submission |
275
+ | `data` | `json` | required | Submitted field values |
276
+ | `submittedById` | `uuid` | null | User who submitted (null for public) |
277
+ | `submittedAt` | `timestamp` | required | Submission timestamp |
278
+ | `isDraft` | `boolean` | false | Draft vs final submission |
279
+ | `metadata` | `simple-json` | null | Additional data |
280
+
281
+ **Indexes:**
282
+ - `formId` - Index for filtering by form
261
283
 
262
- **Note:** FormResult doesn't have `companyId` - company context is derived from the linked Form via `formId`.
284
+ **Note:** FormResult doesn't have `companyId` - company context is derived from the linked Form via `formId`. Company filtering is applied via JOIN in queries.
263
285
 
264
286
  ---
265
287
 
@@ -275,6 +297,31 @@ Stores form submissions:
275
297
  | `PublicFormResponseDto` | Limited fields for public access |
276
298
  | `FormAccessInfoResponseDto` | Access requirements info |
277
299
 
300
+ #### CreateFormDto Fields
301
+
302
+ ```typescript
303
+ class CreateFormDto {
304
+ name: string; // Required, max 255 chars
305
+ description?: string; // Optional, max 500 chars
306
+ slug?: string; // Optional, max 255 chars (unique)
307
+ schema: Record<string, unknown>; // Required - form structure
308
+ accessType?: FormAccessType; // Default: AUTHENTICATED
309
+ actionGroups?: string[]; // For ACTION_GROUP access type
310
+ companyId?: string; // When company feature enabled
311
+ isActive?: boolean; // Default: true
312
+ metadata?: Record<string, unknown>;
313
+ }
314
+ ```
315
+
316
+ #### UpdateFormDto Fields
317
+
318
+ ```typescript
319
+ class UpdateFormDto extends PartialType(CreateFormDto) {
320
+ id: string; // Required
321
+ schemaVersion?: number; // Auto-incremented on schema change
322
+ }
323
+ ```
324
+
278
325
  ### Form Result DTOs
279
326
 
280
327
  | DTO | Purpose |
@@ -287,6 +334,27 @@ Stores form submissions:
287
334
  | `GetResultsByFormDto` | Query results by form ID |
288
335
  | `FormResultResponseDto` | Result response |
289
336
 
337
+ #### SubmitFormDto Fields
338
+
339
+ ```typescript
340
+ class SubmitFormDto {
341
+ formId: string; // Required
342
+ data: Record<string, unknown>; // Required - field values
343
+ isDraft?: boolean; // Default: false
344
+ metadata?: Record<string, unknown>;
345
+ }
346
+ ```
347
+
348
+ #### GetResultsByFormDto Fields
349
+
350
+ ```typescript
351
+ class GetResultsByFormDto {
352
+ formId: string; // Required
353
+ page?: number; // Default: 0
354
+ pageSize?: number; // Default: 10
355
+ }
356
+ ```
357
+
290
358
  ---
291
359
 
292
360
  ## Services
@@ -297,12 +365,21 @@ Provides access to module configuration:
297
365
 
298
366
  ```typescript
299
367
  @Injectable()
300
- export class FormBuilderConfigService {
301
- isCompanyFeatureEnabled(): boolean;
302
- getDatabaseMode(): string;
368
+ export class FormBuilderConfigService implements IModuleConfigService {
369
+ // Check if company feature is enabled (supports per-tenant override)
370
+ isCompanyFeatureEnabled(tenant?: ITenantDatabaseConfig): boolean;
371
+
372
+ // Get database mode ('single' | 'multi-tenant')
373
+ getDatabaseMode(): DatabaseMode;
374
+
375
+ // Check if running in multi-tenant mode
303
376
  isMultiTenant(): boolean;
377
+
378
+ // Get full module options
304
379
  getOptions(): FormBuilderModuleOptions;
305
- getConfig();
380
+
381
+ // Get config section (defaultDatabaseConfig, tenants, etc.)
382
+ getConfig(): IFormBuilderConfig | undefined;
306
383
  }
307
384
  ```
308
385
 
@@ -311,48 +388,111 @@ export class FormBuilderConfigService {
311
388
  Extends `RequestScopedApiService` with form-specific operations:
312
389
 
313
390
  ```typescript
314
- // Get form for public submission
315
- const form = await formService.getPublicForm(formId);
391
+ @Injectable({ scope: Scope.REQUEST })
392
+ export class FormService extends RequestScopedApiService<...> {
393
+ // === Standard CRUD (inherited) ===
394
+ // insert(dto, user), update(dto, user), delete(id, user), getAll(filter, user), getById(id, user)
395
+
396
+ // === Public Access (no auth required) ===
397
+
398
+ // Get form for public submission (accessType must be PUBLIC)
399
+ async getPublicForm(formId: string): Promise<IPublicForm>;
400
+
401
+ // Get public form by slug (no auth required)
402
+ async getPublicFormBySlug(slug: string): Promise<IPublicForm | null>;
403
+
404
+ // Get access info for routing decisions
405
+ async getFormAccessInfo(formId: string): Promise<FormAccessInfoResponseDto>;
406
+
407
+ // === Authenticated Access ===
316
408
 
317
- // Get form for authenticated submission (validates access)
318
- const form = await formService.getAuthenticatedForm(formId, user);
409
+ // Get form for authenticated submission (validates access + permissions)
410
+ async getAuthenticatedForm(formId: string, user: ILoggedUserInfo): Promise<IPublicForm>;
319
411
 
320
- // Get form by slug
321
- const form = await formService.getBySlug('customer-feedback');
412
+ // Get form by slug (requires auth)
413
+ async getBySlug(slug: string): Promise<IForm | null>;
322
414
 
323
- // Get access info (for frontend routing)
324
- const info = await formService.getFormAccessInfo(formId);
415
+ // Get form for submission (internal - validates access type)
416
+ async getFormForSubmission(formId: string, user: ILoggedUserInfo | null): Promise<Form>;
417
+ }
325
418
  ```
326
419
 
327
420
  **Schema Versioning:**
328
421
  - `schemaVersion` auto-increments when schema JSON changes
329
422
  - Comparison uses `JSON.stringify` for deep equality check
423
+ - Version tracked in FormResult snapshots for historical accuracy
330
424
 
331
425
  ### FormResultService
332
426
 
333
427
  Handles form submissions and drafts:
334
428
 
335
429
  ```typescript
336
- // Submit form (validates access type)
337
- const result = await formResultService.submitForm(dto, user, isPublic);
338
-
339
- // Get results by form with pagination
340
- const { data, total } = await formResultService.getByFormId(formId, user, {
341
- page: 0,
342
- pageSize: 10,
343
- });
344
-
345
- // Draft management
346
- const draft = await formResultService.getMyDraft(formId, user);
347
- const updated = await formResultService.updateDraft(draftId, dto, user);
348
- const hasSubmitted = await formResultService.hasUserSubmitted(formId, user);
430
+ @Injectable({ scope: Scope.REQUEST })
431
+ export class FormResultService extends RequestScopedApiService<...> {
432
+ // === Standard CRUD (inherited) ===
433
+ // insert(dto, user), update(dto, user), delete(id, user), getAll(filter, user), getById(id, user)
434
+
435
+ // === Form Submission ===
436
+
437
+ // Submit form (validates access type, handles drafts)
438
+ async submitForm(
439
+ dto: SubmitFormDto,
440
+ user: ILoggedUserInfo | null,
441
+ isPublic?: boolean, // default: false
442
+ ): Promise<IFormResult>;
443
+
444
+ // === Draft Management ===
445
+
446
+ // Get user's draft for a specific form
447
+ async getMyDraft(formId: string, user: ILoggedUserInfo): Promise<IFormResult | null>;
448
+
449
+ // Update existing draft (can convert to final submission)
450
+ async updateDraft(
451
+ draftId: string,
452
+ dto: SubmitFormDto,
453
+ user: ILoggedUserInfo,
454
+ ): Promise<IFormResult>;
455
+
456
+ // === Query Methods ===
457
+
458
+ // Get results by form ID with pagination
459
+ async getByFormId(
460
+ formId: string,
461
+ user: ILoggedUserInfo | null,
462
+ pagination?: { page?: number; pageSize?: number },
463
+ ): Promise<{ data: IFormResult[]; total: number }>;
464
+
465
+ // Check if user has submitted (non-draft) for single response mode
466
+ async hasUserSubmitted(formId: string, user: ILoggedUserInfo): Promise<boolean>;
467
+ }
349
468
  ```
350
469
 
351
470
  **Key behaviors:**
352
471
  - Schema snapshot stored with each submission for historical accuracy
353
472
  - Drafts auto-update if user re-submits as draft
354
- - Final submission deletes existing draft
473
+ - Final submission deletes existing draft (soft delete)
355
474
  - Computed fields applied only on final submission (not drafts)
475
+ - Company filtering via JOIN when company feature enabled
476
+
477
+ ### FormBuilderDataSourceProvider
478
+
479
+ Extends `MultiTenantDataSourceService` for dynamic entity loading:
480
+
481
+ ```typescript
482
+ @Injectable({ scope: Scope.REQUEST })
483
+ export class FormBuilderDataSourceProvider extends MultiTenantDataSourceService {
484
+ // Maintains separate static cache from other modules
485
+ protected static readonly tenantConnections = new Map<string, DataSource>();
486
+ protected static singleDataSource: DataSource | null = null;
487
+
488
+ // Get entities based on company feature flag
489
+ async getFormBuilderEntities(enableCompanyFeature?: boolean): Promise<any[]>;
490
+
491
+ // Inherited from MultiTenantDataSourceService
492
+ async getDataSource(): Promise<DataSource>;
493
+ async getRepository<T>(entity: EntityTarget<T>): Promise<Repository<T>>;
494
+ }
495
+ ```
356
496
 
357
497
  ---
358
498
 
@@ -481,16 +621,37 @@ When company feature is enabled:
481
621
  ### Module Options
482
622
 
483
623
  ```typescript
484
- interface FormBuilderModuleOptions {
485
- global?: boolean; // Make module global
486
- includeController?: boolean; // Include REST controllers
487
- bootstrapAppConfig?: {
488
- databaseMode?: 'single' | 'multi-tenant';
489
- enableCompanyFeature?: boolean;
490
- };
491
- config?: {
492
- defaultDatabaseConfig?: IDatabaseConfig;
493
- };
624
+ interface FormBuilderModuleOptions extends IDynamicModuleConfig {
625
+ global?: boolean; // Make module global
626
+ includeController?: boolean; // Include REST controllers
627
+ bootstrapAppConfig?: IBootstrapAppConfig; // Bootstrap configuration
628
+ config?: IFormBuilderConfig; // Form builder configuration
629
+ }
630
+
631
+ interface IFormBuilderConfig extends IDataSourceServiceOptions {
632
+ // Currently no form-builder specific runtime config
633
+ // Add form-builder specific settings here as needed
634
+ defaultDatabaseConfig?: IDatabaseConfig;
635
+ tenantDefaultDatabaseConfig?: IDatabaseConfig;
636
+ tenants?: ITenantDatabaseConfig[];
637
+ }
638
+ ```
639
+
640
+ ### Async Options
641
+
642
+ ```typescript
643
+ interface FormBuilderModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
644
+ global?: boolean;
645
+ includeController?: boolean;
646
+ bootstrapAppConfig?: IBootstrapAppConfig;
647
+ useFactory?: (...args: any[]) => Promise<IFormBuilderConfig> | IFormBuilderConfig;
648
+ inject?: any[];
649
+ useExisting?: Type<FormBuilderOptionsFactory>;
650
+ useClass?: Type<FormBuilderOptionsFactory>;
651
+ }
652
+
653
+ interface FormBuilderOptionsFactory {
654
+ createFormBuilderOptions(): Promise<IFormBuilderConfig> | IFormBuilderConfig;
494
655
  }
495
656
  ```
496
657
 
@@ -574,28 +735,119 @@ const computedValues = calculateComputedFields(formData, computedFields);
574
735
 
575
736
  **Note:** Backend interface definitions mirror `@flusys/ng-form-builder` interfaces but are defined separately to avoid cross-package dependencies. Both use compatible JSON structures for serialization. See `computed-field.utils.ts` for implementation.
576
737
 
738
+ ### Computed Field Interfaces
739
+
740
+ ```typescript
741
+ interface IComputedField {
742
+ id: string;
743
+ name: string;
744
+ key: string; // Storage key in _computed
745
+ valueType: 'string' | 'number';
746
+ rules: IComputedRule[];
747
+ defaultValue?: string | number | null;
748
+ description?: string;
749
+ }
750
+
751
+ interface IComputedRule {
752
+ id: string;
753
+ condition?: IComputedConditionGroup; // Optional - no condition = always apply
754
+ computation: IComputation;
755
+ }
756
+
757
+ interface IComputedConditionGroup {
758
+ operator: 'AND' | 'OR';
759
+ conditions: IComputedCondition[];
760
+ }
761
+
762
+ interface IComputedCondition {
763
+ fieldId: string;
764
+ comparison: string; // See Condition Operators below
765
+ value: unknown;
766
+ }
767
+
768
+ interface IComputation {
769
+ type: ComputationType;
770
+ config: IDirectValueConfig | IFieldReferenceConfig | IArithmeticConfig;
771
+ }
772
+
773
+ type ComputationType = 'direct' | 'field_reference' | 'arithmetic';
774
+ ```
775
+
776
+ ### Computation Config Types
777
+
778
+ ```typescript
779
+ // Direct value - set a static value
780
+ interface IDirectValueConfig {
781
+ type: 'direct';
782
+ value: string | number;
783
+ }
784
+
785
+ // Field reference - copy value from another field
786
+ interface IFieldReferenceConfig {
787
+ type: 'field_reference';
788
+ fieldId: string;
789
+ }
790
+
791
+ // Arithmetic - calculate from multiple operands
792
+ interface IArithmeticConfig {
793
+ type: 'arithmetic';
794
+ operation: ArithmeticOperation;
795
+ operands: IArithmeticOperand[];
796
+ }
797
+
798
+ interface IArithmeticOperand {
799
+ type: 'field' | 'constant';
800
+ fieldId?: string; // When type = 'field'
801
+ value?: number; // When type = 'constant'
802
+ }
803
+ ```
804
+
577
805
  ### Supported Operations
578
806
 
579
807
  | Type | Description |
580
808
  |------|-------------|
581
809
  | `direct` | Set a static value |
582
810
  | `field_reference` | Copy value from another field |
583
- | `arithmetic` | Calculate using sum, subtract, multiply, divide, average, min, max |
811
+ | `arithmetic` | Calculate using arithmetic operations |
812
+
813
+ ### Arithmetic Operations
814
+
815
+ | Operation | Description |
816
+ |-----------|-------------|
817
+ | `sum` | Add all operand values |
818
+ | `subtract` | Subtract subsequent values from first |
819
+ | `multiply` | Multiply all operand values |
820
+ | `divide` | Divide first value by subsequent values |
821
+ | `average` | Calculate average of all operands |
822
+ | `min` | Get minimum value |
823
+ | `max` | Get maximum value |
824
+ | `increment` | Alias for sum |
825
+ | `decrement` | Alias for subtract |
584
826
 
585
827
  ### Condition Operators
586
828
 
587
829
  Computed fields support conditional rules with these comparison operators:
588
830
 
589
- | Operator | Description |
590
- |----------|-------------|
591
- | `equals`, `not_equals` | Value equality |
592
- | `contains`, `not_contains` | String/array contains |
593
- | `greater_than`, `less_than` | Numeric comparison |
594
- | `greater_or_equal`, `less_or_equal` | Numeric comparison |
595
- | `is_empty`, `is_not_empty` | Null/empty check |
596
- | `is_before`, `is_after` | Date comparison |
597
- | `is_checked`, `is_not_checked` | Boolean/checkbox |
598
- | `is_any_of`, `is_none_of` | Array membership |
831
+ | Operator | Aliases | Description |
832
+ |----------|---------|-------------|
833
+ | `equals` | | Value equality (string-safe) |
834
+ | `not_equals` | | Value inequality |
835
+ | `contains` | | String contains substring |
836
+ | `not_contains` | | String does not contain substring |
837
+ | `starts_with` | | String starts with value |
838
+ | `ends_with` | | String ends with value |
839
+ | `greater_than` | | Numeric greater than |
840
+ | `less_than` | | Numeric less than |
841
+ | `greater_or_equal` | | Numeric greater or equal |
842
+ | `less_or_equal` | | Numeric less or equal |
843
+ | `is_empty` | | Null, undefined, empty string, or empty array |
844
+ | `is_not_empty` | | Has a non-empty value |
845
+ | `is_before` | | Date comparison (before) |
846
+ | `is_after` | | Date comparison (after) |
847
+ | `is_checked` | | Boolean true (or 'true', or 1) |
848
+ | `is_not_checked` | | Boolean false (or 'false', 0, falsy) |
849
+ | `is_any_of` | `in` | Value is in array |
850
+ | `is_none_of` | `not_in` | Value is not in array |
599
851
 
600
852
  ### Data Storage
601
853
 
@@ -711,6 +963,96 @@ async hasUserSubmitted(
711
963
 
712
964
  ---
713
965
 
966
+ ## Swagger Configuration
967
+
968
+ The package includes a Swagger configuration helper that adapts documentation based on feature flags:
969
+
970
+ ```typescript
971
+ import { formBuilderSwaggerConfig } from '@flusys/nestjs-form-builder';
972
+ import { setupSwaggerDocs } from '@flusys/nestjs-core/docs';
973
+
974
+ // In bootstrap
975
+ const bootstrapConfig = { enableCompanyFeature: true };
976
+ setupSwaggerDocs(app, formBuilderSwaggerConfig(bootstrapConfig));
977
+ ```
978
+
979
+ **Features:**
980
+ - Automatically excludes `companyId` fields when company feature is disabled
981
+ - Generates comprehensive API documentation
982
+ - Documents all access types and workflows
983
+
984
+ ### Schema Exclusions
985
+
986
+ When `enableCompanyFeature: false`, these schemas hide `companyId`:
987
+ - `CreateFormDto`
988
+ - `UpdateFormDto`
989
+ - `FormQueryDto`
990
+ - `FormResponseDto`
991
+
992
+ ---
993
+
994
+ ## Permission Utilities
995
+
996
+ The package provides permission validation utilities for action group access:
997
+
998
+ ```typescript
999
+ import { validateUserPermissions } from '@flusys/nestjs-form-builder';
1000
+
1001
+ // Validate user has at least one of the required permissions
1002
+ const hasPermission = await validateUserPermissions(
1003
+ user, // ILoggedUserInfo
1004
+ ['hr.survey.submit', 'admin'], // Required permissions (OR logic)
1005
+ cacheManager, // HybridCache instance
1006
+ enableCompanyFeature, // boolean
1007
+ logger, // Logger instance
1008
+ 'submitting form', // Context for audit logging
1009
+ formId, // Resource ID for audit logging
1010
+ );
1011
+ ```
1012
+
1013
+ **Features:**
1014
+ - Reads permissions from cache (same format as PermissionGuard)
1015
+ - Fail-closed behavior: cache errors result in access denial
1016
+ - Audit logging for permission denials
1017
+
1018
+ ### Permission Cache Key Format
1019
+
1020
+ ```typescript
1021
+ // With company feature enabled
1022
+ `permissions:company:${companyId}:branch:${branchId}:user:${userId}`
1023
+
1024
+ // Without company feature
1025
+ `permissions:user:${userId}`
1026
+ ```
1027
+
1028
+ ---
1029
+
1030
+ ## Controller Security
1031
+
1032
+ Both controllers use `createApiController` with permission-based security:
1033
+
1034
+ ### Form Permissions
1035
+
1036
+ | Operation | Permission |
1037
+ |-----------|------------|
1038
+ | Create | `FORM_PERMISSIONS.CREATE` |
1039
+ | Read | `FORM_PERMISSIONS.READ` |
1040
+ | Update | `FORM_PERMISSIONS.UPDATE` |
1041
+ | Delete | `FORM_PERMISSIONS.DELETE` |
1042
+
1043
+ ### Form Result Permissions
1044
+
1045
+ | Operation | Permission |
1046
+ |-----------|------------|
1047
+ | Create | `FORM_RESULT_PERMISSIONS.CREATE` |
1048
+ | Read | `FORM_RESULT_PERMISSIONS.READ` |
1049
+ | Update | `FORM_RESULT_PERMISSIONS.UPDATE` |
1050
+ | Delete | `FORM_RESULT_PERMISSIONS.DELETE` |
1051
+
1052
+ **Note:** Submit endpoints (`submit`, `submit-public`) don't require these permissions - they use the form's `accessType` for authorization.
1053
+
1054
+ ---
1055
+
714
1056
  ## See Also
715
1057
 
716
1058
  - [ng-form-builder Guide](../../FLUSYS_NG/docs/FORM-BUILDER-GUIDE.md) - Frontend components
@@ -719,4 +1061,4 @@ async hasUserSubmitted(
719
1061
 
720
1062
  ---
721
1063
 
722
- **Last Updated:** 2026-02-21
1064
+ **Last Updated:** 2026-02-25
@@ -80,9 +80,9 @@ _ts_decorate([
80
80
  ], Form.prototype, "schemaVersion", void 0);
81
81
  _ts_decorate([
82
82
  (0, _typeorm.Column)({
83
- type: 'enum',
84
- enum: _formaccesstypeenum.FormAccessType,
85
- default: _formaccesstypeenum.FormAccessType.AUTHENTICATED,
83
+ type: 'varchar',
84
+ length: 50,
85
+ default: 'AUTHENTICATED',
86
86
  name: 'access_type'
87
87
  }),
88
88
  _ts_metadata("design:type", typeof _formaccesstypeenum.FormAccessType === "undefined" ? Object : _formaccesstypeenum.FormAccessType)
@@ -92,14 +92,8 @@ let FormBuilderDataSourceProvider = class FormBuilderDataSourceProvider extends
92
92
  }
93
93
  async getFormBuilderEntities(enableCompanyFeature) {
94
94
  const enable = enableCompanyFeature ?? this.configService.isCompanyFeatureEnabled();
95
- const { Form, FormResult, FormWithCompany } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../entities")));
96
- return enable ? [
97
- FormWithCompany,
98
- FormResult
99
- ] : [
100
- Form,
101
- FormResult
102
- ];
95
+ const { getFormBuilderEntitiesByConfig } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../entities")));
96
+ return getFormBuilderEntitiesByConfig(enable);
103
97
  }
104
98
  async createDataSourceFromConfig(config) {
105
99
  const enableCompanyFeature = this.configService.isCompanyFeatureEnabled(this.getCurrentTenant() ?? undefined);
@@ -70,9 +70,9 @@ _ts_decorate([
70
70
  ], Form.prototype, "schemaVersion", void 0);
71
71
  _ts_decorate([
72
72
  Column({
73
- type: 'enum',
74
- enum: FormAccessType,
75
- default: FormAccessType.AUTHENTICATED,
73
+ type: 'varchar',
74
+ length: 50,
75
+ default: 'AUTHENTICATED',
76
76
  name: 'access_type'
77
77
  }),
78
78
  _ts_metadata("design:type", typeof FormAccessType === "undefined" ? Object : FormAccessType)
@@ -41,14 +41,8 @@ export class FormBuilderDataSourceProvider extends MultiTenantDataSourceService
41
41
  }
42
42
  async getFormBuilderEntities(enableCompanyFeature) {
43
43
  const enable = enableCompanyFeature ?? this.configService.isCompanyFeatureEnabled();
44
- const { Form, FormResult, FormWithCompany } = await import('../entities');
45
- return enable ? [
46
- FormWithCompany,
47
- FormResult
48
- ] : [
49
- Form,
50
- FormResult
51
- ];
44
+ const { getFormBuilderEntitiesByConfig } = await import('../entities');
45
+ return getFormBuilderEntitiesByConfig(enable);
52
46
  }
53
47
  async createDataSourceFromConfig(config) {
54
48
  const enableCompanyFeature = this.configService.isCompanyFeatureEnabled(this.getCurrentTenant() ?? undefined);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flusys/nestjs-form-builder",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "Dynamic form builder module with schema versioning and access control",
5
5
  "main": "cjs/index.js",
6
6
  "module": "fesm/index.js",
@@ -83,7 +83,7 @@
83
83
  "typeorm": "^0.3.0"
84
84
  },
85
85
  "dependencies": {
86
- "@flusys/nestjs-core": "2.0.0",
87
- "@flusys/nestjs-shared": "2.0.0"
86
+ "@flusys/nestjs-core": "3.0.0",
87
+ "@flusys/nestjs-shared": "3.0.0"
88
88
  }
89
89
  }