@aphexcms/cms-core 0.1.17 → 0.2.1
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/api/client.d.ts.map +1 -1
- package/dist/api/client.js +7 -1
- package/dist/api/types.d.ts +2 -0
- package/dist/api/types.d.ts.map +1 -1
- package/dist/cli/generate-types.js +59 -16
- package/dist/cli/index.js +1 -1
- package/dist/client/index.d.ts +0 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +0 -1
- package/dist/components/AdminApp.svelte +278 -45
- package/dist/components/AdminApp.svelte.d.ts +2 -0
- package/dist/components/AdminApp.svelte.d.ts.map +1 -1
- package/dist/components/admin/DocumentEditor.svelte +60 -13
- package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
- package/dist/components/admin/ObjectModal.svelte +15 -4
- package/dist/components/admin/ObjectModal.svelte.d.ts +1 -0
- package/dist/components/admin/ObjectModal.svelte.d.ts.map +1 -1
- package/dist/components/admin/SchemaField.svelte +64 -5
- package/dist/components/admin/SchemaField.svelte.d.ts +1 -0
- package/dist/components/admin/SchemaField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/ArrayField.svelte +72 -17
- package/dist/components/admin/fields/ArrayField.svelte.d.ts +1 -0
- package/dist/components/admin/fields/ArrayField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/DateField.svelte +145 -0
- package/dist/components/admin/fields/DateField.svelte.d.ts +14 -0
- package/dist/components/admin/fields/DateField.svelte.d.ts.map +1 -0
- package/dist/components/admin/fields/DateTimeField.svelte +225 -0
- package/dist/components/admin/fields/DateTimeField.svelte.d.ts +14 -0
- package/dist/components/admin/fields/DateTimeField.svelte.d.ts.map +1 -0
- package/dist/components/admin/fields/ImageField.svelte +20 -7
- package/dist/components/admin/fields/ImageField.svelte.d.ts +1 -0
- package/dist/components/admin/fields/ImageField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/ReferenceField.svelte +1 -1
- package/dist/components/admin/fields/SlugField.svelte +1 -3
- package/dist/components/admin/fields/SlugField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/StringField.svelte +156 -12
- package/dist/components/admin/fields/StringField.svelte.d.ts +3 -2
- package/dist/components/admin/fields/StringField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/URLField.svelte +41 -0
- package/dist/components/admin/fields/URLField.svelte.d.ts +14 -0
- package/dist/components/admin/fields/URLField.svelte.d.ts.map +1 -0
- package/dist/components/index.d.ts +0 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +0 -1
- package/dist/db/interfaces/document.d.ts +0 -2
- package/dist/db/interfaces/document.d.ts.map +1 -1
- package/dist/db/interfaces/index.d.ts +2 -1
- package/dist/db/interfaces/index.d.ts.map +1 -1
- package/dist/db/interfaces/user.d.ts +2 -0
- package/dist/db/interfaces/user.d.ts.map +1 -1
- package/dist/db/utils/reference-resolver.js +1 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +3 -0
- package/dist/field-validation/date-utils.d.ts +30 -0
- package/dist/field-validation/date-utils.d.ts.map +1 -0
- package/dist/field-validation/date-utils.js +147 -0
- package/dist/field-validation/rule.d.ts +4 -0
- package/dist/field-validation/rule.d.ts.map +1 -1
- package/dist/field-validation/rule.js +170 -4
- package/dist/field-validation/utils.d.ts +7 -3
- package/dist/field-validation/utils.d.ts.map +1 -1
- package/dist/field-validation/utils.js +129 -35
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +38 -21
- package/dist/lib/field-validation/date-utils.js +147 -0
- package/dist/lib/field-validation/rule.js +170 -4
- package/dist/lib/field-validation/utils.js +129 -35
- package/dist/local-api/collection-api.d.ts +16 -4
- package/dist/local-api/collection-api.d.ts.map +1 -1
- package/dist/local-api/collection-api.js +51 -17
- package/dist/local-api/index.d.ts +1 -1
- package/dist/local-api/index.d.ts.map +1 -1
- package/dist/routes/assets-cdn.d.ts.map +1 -1
- package/dist/routes/assets-cdn.js +14 -7
- package/dist/routes/assets.d.ts.map +1 -1
- package/dist/routes/assets.js +6 -1
- package/dist/routes/documents-by-id.d.ts.map +1 -1
- package/dist/routes/documents-by-id.js +18 -7
- package/dist/routes/documents-publish.js +2 -2
- package/dist/routes/documents-query.d.ts +3 -1
- package/dist/routes/documents-query.d.ts.map +1 -1
- package/dist/routes/documents-query.js +6 -2
- package/dist/routes/documents.d.ts.map +1 -1
- package/dist/routes/documents.js +20 -4
- package/dist/routes/index.d.ts +1 -0
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +2 -0
- package/dist/routes/user-preferences.d.ts +4 -0
- package/dist/routes/user-preferences.d.ts.map +1 -0
- package/dist/routes/user-preferences.js +77 -0
- package/dist/schema-utils/utils.d.ts +4 -0
- package/dist/schema-utils/utils.d.ts.map +1 -1
- package/dist/schema-utils/utils.js +23 -2
- package/dist/schema-utils/validator.d.ts +4 -0
- package/dist/schema-utils/validator.d.ts.map +1 -1
- package/dist/schema-utils/validator.js +120 -0
- package/dist/types/filters.d.ts +13 -0
- package/dist/types/filters.d.ts.map +1 -1
- package/dist/types/organization.d.ts +3 -0
- package/dist/types/organization.d.ts.map +1 -1
- package/dist/types/schemas.d.ts +67 -7
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/utils/default-orderings.d.ts +10 -0
- package/dist/utils/default-orderings.d.ts.map +1 -0
- package/dist/utils/default-orderings.js +63 -0
- package/dist/utils/field-defaults.d.ts +8 -0
- package/dist/utils/field-defaults.d.ts.map +1 -0
- package/dist/utils/field-defaults.js +20 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/initial-value-helpers.d.ts +50 -0
- package/dist/utils/initial-value-helpers.d.ts.map +1 -0
- package/dist/utils/initial-value-helpers.js +70 -0
- package/package.json +6 -4
- package/dist/components/admin/DocumentTypesList.svelte +0 -97
- package/dist/components/admin/DocumentTypesList.svelte.d.ts +0 -14
- package/dist/components/admin/DocumentTypesList.svelte.d.ts.map +0 -1
|
@@ -24,8 +24,6 @@ export interface UpdateDocumentData {
|
|
|
24
24
|
* Document adapter interface for document-specific operations
|
|
25
25
|
*/
|
|
26
26
|
export interface DocumentAdapter {
|
|
27
|
-
findManyDoc(organizationId: string, filters?: Omit<DocumentFilters, 'organizationId'>): Promise<Document[]>;
|
|
28
|
-
findByDocId(organizationId: string, id: string, depth?: number): Promise<Document | null>;
|
|
29
27
|
createDocument(data: CreateDocumentData): Promise<Document>;
|
|
30
28
|
updateDocDraft(organizationId: string, id: string, data: any, updatedBy?: string): Promise<Document | null>;
|
|
31
29
|
deleteDocById(organizationId: string, id: string): Promise<boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../../../src/lib/db/interfaces/document.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAE1E,MAAM,WAAW,eAAe;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,kBAAkB;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,GAAG,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IAClC,SAAS,CAAC,EAAE,GAAG,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAE/B,
|
|
1
|
+
{"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../../../src/lib/db/interfaces/document.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAE1E,MAAM,WAAW,eAAe;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,kBAAkB;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,GAAG,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IAClC,SAAS,CAAC,EAAE,GAAG,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAE/B,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC5D,cAAc,CACb,cAAc,EAAE,MAAM,EACtB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,GAAG,EACT,SAAS,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IAC5B,aAAa,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAGpE,UAAU,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IACzE,YAAY,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IAG3E,eAAe,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvE,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAG5E;;;;;;OAMG;IACH,mBAAmB,CAClB,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEjC;;;;;;OAMG;IACH,mBAAmB,CAClB,cAAc,EAAE,MAAM,EACtB,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAC5B,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IAE5B;;;;;;OAMG;IACH,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC/F"}
|
|
@@ -20,6 +20,7 @@ export interface DatabaseAdapter extends DocumentAdapter, AssetAdapter, UserProf
|
|
|
20
20
|
* Initialize RLS (enable/disable) on tables - call after migrations
|
|
21
21
|
*/
|
|
22
22
|
initializeRLS?(): Promise<void>;
|
|
23
|
+
hierarchyEnabled: boolean;
|
|
23
24
|
/**
|
|
24
25
|
* Execute a function within a transaction with organization context set for RLS
|
|
25
26
|
* Ensures proper isolation with connection pooling
|
|
@@ -28,7 +29,7 @@ export interface DatabaseAdapter extends DocumentAdapter, AssetAdapter, UserProf
|
|
|
28
29
|
/**
|
|
29
30
|
* Get all child organizations for a parent (for hierarchy support)
|
|
30
31
|
*/
|
|
31
|
-
getChildOrganizations
|
|
32
|
+
getChildOrganizations(parentOrganizationId: string): Promise<string[]>;
|
|
32
33
|
/**
|
|
33
34
|
* Check if any user profiles exist in the system (for first-user detection)
|
|
34
35
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/db/interfaces/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAG1D,YAAY,EACX,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC9E,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AACrE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D;;;GAGG;AACH,MAAM,WAAW,eAChB,SAAQ,eAAe,EACtB,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,mBAAmB;IAEpB,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,UAAU,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAG7B,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAG9B;;OAEG;IACH,aAAa,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/db/interfaces/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAG1D,YAAY,EACX,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC9E,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AACrE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D;;;GAGG;AACH,MAAM,WAAW,eAChB,SAAQ,eAAe,EACtB,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,mBAAmB;IAEpB,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,UAAU,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAG7B,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAG9B;;OAEG;IACH,aAAa,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,gBAAgB,EAAE,OAAO,CAAC;IAE1B;;;OAGG;IACH,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAE7E;;OAEG;IACH,qBAAqB,CAAC,oBAAoB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAEvE;;OAEG;IACH,kBAAkB,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CACxC;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,IAAI,eAAe,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,OAAO,CAAC,EAAE;QACT,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,GAAG,CAAC,EAAE,OAAO,CAAC;QACd,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACnB,CAAC;CACF;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,QAAQ,IAAI,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,4BAA6B,SAAQ,eAAe;IACpE,gBAAgB,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACjD,eAAe,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC7E"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { UserProfile } from '../../types/index.js';
|
|
2
|
+
import type { UserSessionPreferences } from '../../types/organization.js';
|
|
2
3
|
export interface NewUserProfileData {
|
|
3
4
|
userId: string;
|
|
4
5
|
role?: 'super_admin' | 'admin' | 'editor' | 'viewer';
|
|
@@ -11,5 +12,6 @@ export interface UserProfileAdapter {
|
|
|
11
12
|
createUserProfile(data: NewUserProfileData): Promise<UserProfile>;
|
|
12
13
|
findUserProfileById(userId: string): Promise<UserProfile | null>;
|
|
13
14
|
deleteUserProfile(userId: string): Promise<boolean>;
|
|
15
|
+
updateUserPreferences(userId: string, preferences: Partial<UserSessionPreferences>): Promise<void>;
|
|
14
16
|
}
|
|
15
17
|
//# sourceMappingURL=user.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../src/lib/db/interfaces/user.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../src/lib/db/interfaces/user.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAEvE,MAAM,WAAW,kBAAkB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAClE,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACjE,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,qBAAqB,CACpB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,OAAO,CAAC,sBAAsB,CAAC,GAC1C,OAAO,CAAC,IAAI,CAAC,CAAC;CACjB"}
|
|
@@ -52,7 +52,7 @@ async function resolveDataReferences(data, adapter, organizationId, options) {
|
|
|
52
52
|
if (typeof value === 'string' && key !== '_type' && key !== '_key') {
|
|
53
53
|
// Try to fetch the referenced document
|
|
54
54
|
try {
|
|
55
|
-
const referencedDoc = await adapter.
|
|
55
|
+
const referencedDoc = await adapter.findByDocIdAdvanced(organizationId, value);
|
|
56
56
|
if (referencedDoc) {
|
|
57
57
|
// Recursively resolve nested references
|
|
58
58
|
resolved[key] = await resolveReferences(referencedDoc, adapter, organizationId, options);
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/lib/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/lib/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAG7D,qBAAa,SAAS;IACrB,OAAO,CAAC,EAAE,CAAkB;IACrB,MAAM,EAAE,SAAS,CAAC;gBAEb,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe;IAMzD,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAUlC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B3B,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAIvD,WAAW,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAI1C,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAI9C,iBAAiB,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAI1F,eAAe,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAG9F;AAKD,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,GAAG,SAAS,CAKlF;AAED,wBAAgB,MAAM,IAAI,SAAS,CAKlC"}
|
package/dist/engine.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { validateSchemaReferences } from './schema-utils/validator.js';
|
|
1
2
|
export class CMSEngine {
|
|
2
3
|
db;
|
|
3
4
|
config;
|
|
@@ -17,6 +18,8 @@ export class CMSEngine {
|
|
|
17
18
|
// Initialize CMS - register schema types in database
|
|
18
19
|
async initialize() {
|
|
19
20
|
console.log('🚀 Initializing CMS...');
|
|
21
|
+
// Validate schemas before syncing to database
|
|
22
|
+
validateSchemaReferences(this.config.schemaTypes);
|
|
20
23
|
// Get existing schemas from database
|
|
21
24
|
const existingSchemas = await this.db.listSchemas();
|
|
22
25
|
const existingNames = new Set(existingSchemas.map((s) => s.name));
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { SchemaType } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Convert a date value to user format for validation
|
|
4
|
+
* Handles both ISO format and user format inputs
|
|
5
|
+
*/
|
|
6
|
+
export declare function convertDateToUserFormat(value: string, userFormat: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Convert a date value to ISO format for storage
|
|
9
|
+
* Returns ISO if already valid, or original value if invalid
|
|
10
|
+
*/
|
|
11
|
+
export declare function convertDateToISO(value: string, userFormat: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Convert a datetime value to user format for validation
|
|
14
|
+
* Handles both ISO datetime and user format inputs
|
|
15
|
+
*/
|
|
16
|
+
export declare function convertDateTimeToUserFormat(value: string, dateFormat: string, timeFormat?: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Convert a datetime value to ISO UTC format for storage
|
|
19
|
+
* Returns ISO UTC if already valid, or original value if invalid
|
|
20
|
+
*/
|
|
21
|
+
export declare function convertDateTimeToISO(value: string, dateFormat: string, timeFormat?: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Normalize date fields in data object
|
|
24
|
+
* Converts dates to ISO for storage and creates a parallel object with user-formatted dates for validation
|
|
25
|
+
*/
|
|
26
|
+
export declare function normalizeDateFields(data: Record<string, any>, schema: SchemaType): {
|
|
27
|
+
normalizedData: Record<string, any>;
|
|
28
|
+
dataForValidation: Record<string, any>;
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=date-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-utils.d.ts","sourceRoot":"","sources":["../../src/lib/field-validation/date-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAA4B,MAAM,gBAAgB,CAAC;AAQ3E;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAiBjF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAiB1E;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAC1C,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,UAAU,GAAE,MAAgB,GAC1B,MAAM,CAmBR;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,UAAU,GAAE,MAAgB,GAC1B,MAAM,CA0BR;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,MAAM,EAAE,UAAU,GAChB;IACF,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACpC,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACvC,CA8DA"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
|
3
|
+
import utc from 'dayjs/plugin/utc';
|
|
4
|
+
dayjs.extend(customParseFormat);
|
|
5
|
+
dayjs.extend(utc);
|
|
6
|
+
/**
|
|
7
|
+
* Convert a date value to user format for validation
|
|
8
|
+
* Handles both ISO format and user format inputs
|
|
9
|
+
*/
|
|
10
|
+
export function convertDateToUserFormat(value, userFormat) {
|
|
11
|
+
// Try parsing as ISO format first (what DateField stores)
|
|
12
|
+
// Use strict mode to reject invalid dates
|
|
13
|
+
const parsedISO = dayjs(value, 'YYYY-MM-DD', true);
|
|
14
|
+
if (parsedISO.isValid()) {
|
|
15
|
+
return parsedISO.format(userFormat);
|
|
16
|
+
}
|
|
17
|
+
// Try parsing as user format (what API might send)
|
|
18
|
+
// Use strict mode to reject invalid dates like Feb 31st
|
|
19
|
+
const parsedUser = dayjs(value, userFormat, true);
|
|
20
|
+
if (parsedUser.isValid()) {
|
|
21
|
+
return value; // Already in user format
|
|
22
|
+
}
|
|
23
|
+
// Invalid date - return as-is for validation to catch
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Convert a date value to ISO format for storage
|
|
28
|
+
* Returns ISO if already valid, or original value if invalid
|
|
29
|
+
*/
|
|
30
|
+
export function convertDateToISO(value, userFormat) {
|
|
31
|
+
// Try parsing as user format first (for API consumers)
|
|
32
|
+
// Use strict mode to reject invalid dates like Feb 31st
|
|
33
|
+
const parsedUser = dayjs(value, userFormat, true);
|
|
34
|
+
if (parsedUser.isValid()) {
|
|
35
|
+
return parsedUser.format('YYYY-MM-DD');
|
|
36
|
+
}
|
|
37
|
+
// Try parsing as ISO (for DateField component)
|
|
38
|
+
// Use strict mode
|
|
39
|
+
const parsedISO = dayjs(value, 'YYYY-MM-DD', true);
|
|
40
|
+
if (parsedISO.isValid()) {
|
|
41
|
+
return value; // Already ISO
|
|
42
|
+
}
|
|
43
|
+
// Invalid date - return as-is
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Convert a datetime value to user format for validation
|
|
48
|
+
* Handles both ISO datetime and user format inputs
|
|
49
|
+
*/
|
|
50
|
+
export function convertDateTimeToUserFormat(value, dateFormat, timeFormat = 'HH:mm') {
|
|
51
|
+
const userFormat = `${dateFormat} ${timeFormat}`;
|
|
52
|
+
// Try parsing as ISO datetime first (what DateTimeField stores)
|
|
53
|
+
// Use strict mode to reject invalid dates
|
|
54
|
+
const parsedISO = dayjs(value, 'YYYY-MM-DDTHH:mm:ss[Z]', true);
|
|
55
|
+
if (parsedISO.isValid()) {
|
|
56
|
+
return parsedISO.format(userFormat);
|
|
57
|
+
}
|
|
58
|
+
// Try parsing as user format (what API might send)
|
|
59
|
+
// Use strict mode to reject invalid dates like Feb 31st
|
|
60
|
+
const parsedUser = dayjs(value, userFormat, true);
|
|
61
|
+
if (parsedUser.isValid()) {
|
|
62
|
+
return value; // Already in user format
|
|
63
|
+
}
|
|
64
|
+
// Invalid datetime - return as-is for validation to catch
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Convert a datetime value to ISO UTC format for storage
|
|
69
|
+
* Returns ISO UTC if already valid, or original value if invalid
|
|
70
|
+
*/
|
|
71
|
+
export function convertDateTimeToISO(value, dateFormat, timeFormat = 'HH:mm') {
|
|
72
|
+
const userFormat = `${dateFormat} ${timeFormat}`;
|
|
73
|
+
console.log('[convertDateTimeToISO]', { value, userFormat });
|
|
74
|
+
// Try parsing as user format first (for API consumers)
|
|
75
|
+
// Use strict mode to reject invalid dates like Feb 31st
|
|
76
|
+
const parsedUser = dayjs(value, userFormat, true);
|
|
77
|
+
if (parsedUser.isValid()) {
|
|
78
|
+
const result = parsedUser.utc().format('YYYY-MM-DDTHH:mm:ss[Z]');
|
|
79
|
+
console.log('[convertDateTimeToISO] User format parse successful, converting to UTC:', result);
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
// Try parsing as ISO datetime (for DateTimeField component)
|
|
83
|
+
// Use strict mode with ISO format
|
|
84
|
+
const parsedISO = dayjs(value, 'YYYY-MM-DDTHH:mm:ss[Z]', true);
|
|
85
|
+
if (parsedISO.isValid()) {
|
|
86
|
+
const result = parsedISO.utc().format('YYYY-MM-DDTHH:mm:ss[Z]');
|
|
87
|
+
console.log('[convertDateTimeToISO] ISO parse successful:', result);
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
// Invalid datetime - return as-is
|
|
91
|
+
console.log('[convertDateTimeToISO] Invalid - returning as-is');
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Normalize date fields in data object
|
|
96
|
+
* Converts dates to ISO for storage and creates a parallel object with user-formatted dates for validation
|
|
97
|
+
*/
|
|
98
|
+
export function normalizeDateFields(data, schema) {
|
|
99
|
+
const normalizedData = { ...data };
|
|
100
|
+
const dataForValidation = { ...data };
|
|
101
|
+
console.log('[normalizeDateFields] Starting normalization...');
|
|
102
|
+
console.log('[normalizeDateFields] Input data:', data);
|
|
103
|
+
for (const field of schema.fields) {
|
|
104
|
+
if (field.type === 'date' && normalizedData[field.name]) {
|
|
105
|
+
const dateField = field;
|
|
106
|
+
const userFormat = dateField.options?.dateFormat || 'YYYY-MM-DD';
|
|
107
|
+
const dateValue = normalizedData[field.name];
|
|
108
|
+
console.log(`[normalizeDateFields] Processing DATE field "${field.name}"`, {
|
|
109
|
+
originalValue: dateValue,
|
|
110
|
+
userFormat
|
|
111
|
+
});
|
|
112
|
+
if (typeof dateValue === 'string') {
|
|
113
|
+
normalizedData[field.name] = convertDateToISO(dateValue, userFormat);
|
|
114
|
+
dataForValidation[field.name] = convertDateToUserFormat(dateValue, userFormat);
|
|
115
|
+
console.log(`[normalizeDateFields] Converted DATE field "${field.name}"`, {
|
|
116
|
+
normalizedValue: normalizedData[field.name],
|
|
117
|
+
validationValue: dataForValidation[field.name]
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else if (field.type === 'datetime' && normalizedData[field.name]) {
|
|
122
|
+
const dateTimeField = field;
|
|
123
|
+
const dateFormat = dateTimeField.options?.dateFormat || 'YYYY-MM-DD';
|
|
124
|
+
const timeFormat = dateTimeField.options?.timeFormat || 'HH:mm';
|
|
125
|
+
const dateTimeValue = normalizedData[field.name];
|
|
126
|
+
console.log(`[normalizeDateFields] Processing DATETIME field "${field.name}"`, {
|
|
127
|
+
originalValue: dateTimeValue,
|
|
128
|
+
dateFormat,
|
|
129
|
+
timeFormat,
|
|
130
|
+
combinedFormat: `${dateFormat} ${timeFormat}`
|
|
131
|
+
});
|
|
132
|
+
if (typeof dateTimeValue === 'string') {
|
|
133
|
+
normalizedData[field.name] = convertDateTimeToISO(dateTimeValue, dateFormat, timeFormat);
|
|
134
|
+
dataForValidation[field.name] = convertDateTimeToUserFormat(dateTimeValue, dateFormat, timeFormat);
|
|
135
|
+
console.log(`[normalizeDateFields] Converted DATETIME field "${field.name}"`, {
|
|
136
|
+
normalizedValue: normalizedData[field.name],
|
|
137
|
+
validationValue: dataForValidation[field.name]
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
console.log('[normalizeDateFields] Final result:', {
|
|
143
|
+
normalizedData,
|
|
144
|
+
dataForValidation
|
|
145
|
+
});
|
|
146
|
+
return { normalizedData, dataForValidation };
|
|
147
|
+
}
|
|
@@ -28,6 +28,7 @@ export declare class Rule {
|
|
|
28
28
|
min(len: number | string | FieldReference): Rule;
|
|
29
29
|
max(len: number | string | FieldReference): Rule;
|
|
30
30
|
length(len: number | FieldReference): Rule;
|
|
31
|
+
unique(): Rule;
|
|
31
32
|
email(): Rule;
|
|
32
33
|
uri(options?: {
|
|
33
34
|
scheme?: RegExp[];
|
|
@@ -39,6 +40,8 @@ export declare class Rule {
|
|
|
39
40
|
integer(): Rule;
|
|
40
41
|
greaterThan(num: number | FieldReference): Rule;
|
|
41
42
|
lessThan(num: number | FieldReference): Rule;
|
|
43
|
+
date(format?: string): Rule;
|
|
44
|
+
datetime(dateFormat?: string, timeFormat?: string): Rule;
|
|
42
45
|
custom<T = unknown>(fn: CustomValidator<T>): Rule;
|
|
43
46
|
error(message?: string): Rule;
|
|
44
47
|
warning(message?: string): Rule;
|
|
@@ -47,5 +50,6 @@ export declare class Rule {
|
|
|
47
50
|
validate(value: unknown, context?: ValidationContext): Promise<ValidationMarker[]>;
|
|
48
51
|
private validateRule;
|
|
49
52
|
isRequired(): boolean;
|
|
53
|
+
private normalizeForComparison;
|
|
50
54
|
}
|
|
51
55
|
//# sourceMappingURL=rule.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule.d.ts","sourceRoot":"","sources":["../../src/lib/field-validation/rule.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rule.d.ts","sourceRoot":"","sources":["../../src/lib/field-validation/rule.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,gBAAgB;IAChC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAEjC,QAAQ,CAAC,EAAE,GAAG,CAAC;IAEf,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,OAAO;IAC3C,CACC,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,iBAAiB,GACxB,gBAAgB,EAAE,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC,gBAAgB,EAAE,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CAC1F;AAED,MAAM,WAAW,cAAc;IAC9B,gBAAgB,EAAE,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACxB;AAED,qBAAa,IAAI;IAChB,OAAO,CAAC,SAAS,CAAkB;IAEnC,OAAO,CAAC,MAAM,CAAmE;IACjF,OAAO,CAAC,MAAM,CAAyC;IACvD,OAAO,CAAC,QAAQ,CAAC,CAAS;IAE1B,MAAM,CAAC,SAAS,SAA4B;IAE5C,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,cAAc;IAO5D,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,cAAc;IAIrD,QAAQ,IAAI,IAAI;IAMhB,QAAQ,IAAI,IAAI;IAMhB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,cAAc,GAAG,IAAI;IAMhD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,cAAc,GAAG,IAAI;IAMhD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAM1C,MAAM,IAAI,IAAI;IAMd,KAAK,IAAI,IAAI;IAMb,GAAG,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,aAAa,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;IAMnE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAM3C,QAAQ,IAAI,IAAI;IAMhB,QAAQ,IAAI,IAAI;IAMhB,OAAO,IAAI,IAAI;IAMf,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAM/C,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAM5C,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAM3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI;IAOxD,MAAM,CAAC,CAAC,GAAG,OAAO,EAAE,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,IAAI;IAMjD,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAO7B,OAAO,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAO/B,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAO5B,KAAK,IAAI,IAAI;IASP,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;YAwC9E,YAAY;IAgO1B,UAAU,IAAI,OAAO;IAKrB,OAAO,CAAC,sBAAsB;CAkB9B"}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// Sanity-style validation Rule implementation
|
|
2
|
+
import dayjs from 'dayjs';
|
|
3
|
+
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
|
4
|
+
// Enable strict parsing
|
|
5
|
+
dayjs.extend(customParseFormat);
|
|
1
6
|
export class Rule {
|
|
2
7
|
_required = false;
|
|
3
8
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -39,6 +44,11 @@ export class Rule {
|
|
|
39
44
|
newRule._rules.push({ type: 'length', constraint: len });
|
|
40
45
|
return newRule;
|
|
41
46
|
}
|
|
47
|
+
unique() {
|
|
48
|
+
const newRule = this.clone();
|
|
49
|
+
newRule._rules.push({ type: 'unique' });
|
|
50
|
+
return newRule;
|
|
51
|
+
}
|
|
42
52
|
email() {
|
|
43
53
|
const newRule = this.clone();
|
|
44
54
|
newRule._rules.push({ type: 'email' });
|
|
@@ -79,6 +89,17 @@ export class Rule {
|
|
|
79
89
|
newRule._rules.push({ type: 'lessThan', constraint: num });
|
|
80
90
|
return newRule;
|
|
81
91
|
}
|
|
92
|
+
date(format) {
|
|
93
|
+
const newRule = this.clone();
|
|
94
|
+
newRule._rules.push({ type: 'date', constraint: format || 'YYYY-MM-DD' });
|
|
95
|
+
return newRule;
|
|
96
|
+
}
|
|
97
|
+
datetime(dateFormat, timeFormat) {
|
|
98
|
+
const newRule = this.clone();
|
|
99
|
+
const fullFormat = `${dateFormat || 'YYYY-MM-DD'} ${timeFormat || 'HH:mm'}`;
|
|
100
|
+
newRule._rules.push({ type: 'datetime', constraint: fullFormat });
|
|
101
|
+
return newRule;
|
|
102
|
+
}
|
|
82
103
|
custom(fn) {
|
|
83
104
|
const newRule = this.clone();
|
|
84
105
|
newRule._rules.push({ type: 'custom', constraint: fn });
|
|
@@ -155,6 +176,9 @@ export class Rule {
|
|
|
155
176
|
if (typeof value === 'number' && value < rule.constraint) {
|
|
156
177
|
return `Must be at least ${rule.constraint}`;
|
|
157
178
|
}
|
|
179
|
+
if (Array.isArray(value) && value.length < rule.constraint) {
|
|
180
|
+
return `Must have at least ${rule.constraint} item${rule.constraint === 1 ? '' : 's'}`;
|
|
181
|
+
}
|
|
158
182
|
break;
|
|
159
183
|
case 'max':
|
|
160
184
|
if (typeof value === 'string' && value.length > rule.constraint) {
|
|
@@ -163,6 +187,31 @@ export class Rule {
|
|
|
163
187
|
if (typeof value === 'number' && value > rule.constraint) {
|
|
164
188
|
return `Must be at most ${rule.constraint}`;
|
|
165
189
|
}
|
|
190
|
+
if (Array.isArray(value) && value.length > rule.constraint) {
|
|
191
|
+
return `Must have at most ${rule.constraint} item${rule.constraint === 1 ? '' : 's'}`;
|
|
192
|
+
}
|
|
193
|
+
break;
|
|
194
|
+
case 'length':
|
|
195
|
+
if (Array.isArray(value) && value.length !== rule.constraint) {
|
|
196
|
+
return `Must have exactly ${rule.constraint} item${rule.constraint === 1 ? '' : 's'}`;
|
|
197
|
+
}
|
|
198
|
+
if (typeof value === 'string' && value.length !== rule.constraint) {
|
|
199
|
+
return `Must be exactly ${rule.constraint} characters`;
|
|
200
|
+
}
|
|
201
|
+
break;
|
|
202
|
+
case 'unique':
|
|
203
|
+
if (Array.isArray(value)) {
|
|
204
|
+
const seen = new Set();
|
|
205
|
+
for (const item of value) {
|
|
206
|
+
// Deep comparison excluding _key property
|
|
207
|
+
const normalized = this.normalizeForComparison(item);
|
|
208
|
+
const serialized = JSON.stringify(normalized);
|
|
209
|
+
if (seen.has(serialized)) {
|
|
210
|
+
return 'All items must be unique';
|
|
211
|
+
}
|
|
212
|
+
seen.add(serialized);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
166
215
|
break;
|
|
167
216
|
case 'email':
|
|
168
217
|
if (typeof value === 'string' && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
|
|
@@ -171,11 +220,57 @@ export class Rule {
|
|
|
171
220
|
break;
|
|
172
221
|
case 'uri':
|
|
173
222
|
if (typeof value === 'string') {
|
|
174
|
-
|
|
175
|
-
|
|
223
|
+
const opts = rule.constraint || {};
|
|
224
|
+
const schemes = opts.scheme || [/^https?$/];
|
|
225
|
+
const allowRelative = opts.allowRelative || false;
|
|
226
|
+
const relativeOnly = opts.relativeOnly || false;
|
|
227
|
+
// Check if URL is relative (doesn't have a scheme)
|
|
228
|
+
const hasScheme = /^[a-z][a-z0-9+.-]*:/i.test(value);
|
|
229
|
+
const isProtocolRelative = value.startsWith('//');
|
|
230
|
+
if (relativeOnly) {
|
|
231
|
+
// Only allow relative URLs
|
|
232
|
+
if (hasScheme || isProtocolRelative) {
|
|
233
|
+
return 'Must be a relative URL';
|
|
234
|
+
}
|
|
235
|
+
// Validate it's a reasonable path
|
|
236
|
+
if (!value.startsWith('/') &&
|
|
237
|
+
!value.startsWith('.') &&
|
|
238
|
+
!value.startsWith('#') &&
|
|
239
|
+
!value.startsWith('?')) {
|
|
240
|
+
return 'Must be a relative URL starting with /, ., #, or ?';
|
|
241
|
+
}
|
|
176
242
|
}
|
|
177
|
-
|
|
178
|
-
|
|
243
|
+
else if (!hasScheme && !isProtocolRelative) {
|
|
244
|
+
// It's a relative URL
|
|
245
|
+
if (!allowRelative) {
|
|
246
|
+
return 'Must be an absolute URL';
|
|
247
|
+
}
|
|
248
|
+
// Validate it's a reasonable relative path
|
|
249
|
+
if (!value.startsWith('/') &&
|
|
250
|
+
!value.startsWith('.') &&
|
|
251
|
+
!value.startsWith('#') &&
|
|
252
|
+
!value.startsWith('?')) {
|
|
253
|
+
return 'Must be a valid relative URL';
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
// It's an absolute URL, validate with URL constructor
|
|
258
|
+
try {
|
|
259
|
+
const url = new URL(value);
|
|
260
|
+
// Extract scheme without the trailing colon
|
|
261
|
+
const urlScheme = url.protocol.slice(0, -1);
|
|
262
|
+
// Check if scheme is allowed
|
|
263
|
+
const schemeMatches = schemes.some((s) => s instanceof RegExp ? s.test(urlScheme) : s === urlScheme);
|
|
264
|
+
if (!schemeMatches) {
|
|
265
|
+
const schemeList = schemes
|
|
266
|
+
.map((s) => (s instanceof RegExp ? s.toString() : s))
|
|
267
|
+
.join(', ');
|
|
268
|
+
return `URL scheme must be one of: ${schemeList}`;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
return 'Must be a valid URL';
|
|
273
|
+
}
|
|
179
274
|
}
|
|
180
275
|
}
|
|
181
276
|
break;
|
|
@@ -199,6 +294,58 @@ export class Rule {
|
|
|
199
294
|
return 'Must be an integer';
|
|
200
295
|
}
|
|
201
296
|
break;
|
|
297
|
+
case 'date': {
|
|
298
|
+
if (typeof value === 'string') {
|
|
299
|
+
const format = rule.constraint || 'YYYY-MM-DD';
|
|
300
|
+
console.log('[Rule.validate] DATE validation', { value, format });
|
|
301
|
+
// Parse with strict mode
|
|
302
|
+
const parsed = dayjs(value, format, true);
|
|
303
|
+
console.log('[Rule.validate] DATE parsed', {
|
|
304
|
+
isValid: parsed.isValid(),
|
|
305
|
+
parsed: parsed.format()
|
|
306
|
+
});
|
|
307
|
+
if (!parsed.isValid()) {
|
|
308
|
+
console.log('[Rule.validate] DATE validation FAILED - invalid format');
|
|
309
|
+
return `Invalid date format. Expected: ${format}`;
|
|
310
|
+
}
|
|
311
|
+
// Verify the parsed date matches the input (catches invalid dates like 2025-02-31)
|
|
312
|
+
if (parsed.format(format) !== value) {
|
|
313
|
+
console.log('[Rule.validate] DATE validation FAILED - format mismatch', {
|
|
314
|
+
expected: value,
|
|
315
|
+
got: parsed.format(format)
|
|
316
|
+
});
|
|
317
|
+
return `Invalid date. Expected format: ${format}`;
|
|
318
|
+
}
|
|
319
|
+
console.log('[Rule.validate] DATE validation PASSED');
|
|
320
|
+
}
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
case 'datetime': {
|
|
324
|
+
if (typeof value === 'string') {
|
|
325
|
+
const format = rule.constraint || 'YYYY-MM-DD HH:mm';
|
|
326
|
+
console.log('[Rule.validate] DATETIME validation', { value, format });
|
|
327
|
+
// Parse with strict mode
|
|
328
|
+
const parsed = dayjs(value, format, true);
|
|
329
|
+
console.log('[Rule.validate] DATETIME parsed', {
|
|
330
|
+
isValid: parsed.isValid(),
|
|
331
|
+
parsed: parsed.format()
|
|
332
|
+
});
|
|
333
|
+
if (!parsed.isValid()) {
|
|
334
|
+
console.log('[Rule.validate] DATETIME validation FAILED - invalid format');
|
|
335
|
+
return `Invalid datetime format. Expected: ${format}`;
|
|
336
|
+
}
|
|
337
|
+
// Verify the parsed datetime matches the input (catches invalid dates like 2025-02-31 23:59)
|
|
338
|
+
if (parsed.format(format) !== value) {
|
|
339
|
+
console.log('[Rule.validate] DATETIME validation FAILED - format mismatch', {
|
|
340
|
+
expected: value,
|
|
341
|
+
got: parsed.format(format)
|
|
342
|
+
});
|
|
343
|
+
return `Invalid datetime. Expected format: ${format}`;
|
|
344
|
+
}
|
|
345
|
+
console.log('[Rule.validate] DATETIME validation PASSED');
|
|
346
|
+
}
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
202
349
|
case 'custom': {
|
|
203
350
|
const customResult = await rule.constraint(value, context);
|
|
204
351
|
if (customResult === false) {
|
|
@@ -218,4 +365,23 @@ export class Rule {
|
|
|
218
365
|
isRequired() {
|
|
219
366
|
return this._required;
|
|
220
367
|
}
|
|
368
|
+
// Helper method to normalize objects for comparison (exclude _key)
|
|
369
|
+
normalizeForComparison(value) {
|
|
370
|
+
if (value === null || value === undefined) {
|
|
371
|
+
return value;
|
|
372
|
+
}
|
|
373
|
+
if (Array.isArray(value)) {
|
|
374
|
+
return value.map((item) => this.normalizeForComparison(item));
|
|
375
|
+
}
|
|
376
|
+
if (typeof value === 'object') {
|
|
377
|
+
const normalized = {};
|
|
378
|
+
for (const [key, val] of Object.entries(value)) {
|
|
379
|
+
if (key !== '_key') {
|
|
380
|
+
normalized[key] = this.normalizeForComparison(val);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return normalized;
|
|
384
|
+
}
|
|
385
|
+
return value;
|
|
386
|
+
}
|
|
221
387
|
}
|
|
@@ -9,6 +9,7 @@ export interface DocumentValidationResult {
|
|
|
9
9
|
field: string;
|
|
10
10
|
errors: string[];
|
|
11
11
|
}>;
|
|
12
|
+
normalizedData: Record<string, any>;
|
|
12
13
|
}
|
|
13
14
|
/**
|
|
14
15
|
* Check if a field is required based on its validation rules
|
|
@@ -27,13 +28,16 @@ export declare function validateField(field: Field, value: any, context?: any):
|
|
|
27
28
|
export declare function getValidationClasses(hasErrors: boolean): string;
|
|
28
29
|
/**
|
|
29
30
|
* Validate an entire document's data against a schema
|
|
30
|
-
* This function
|
|
31
|
-
*
|
|
31
|
+
* This function:
|
|
32
|
+
* 1. Normalizes date fields (converts user format to ISO for storage)
|
|
33
|
+
* 2. Converts ISO dates to user format for validation
|
|
34
|
+
* 3. Validates all fields and returns errors
|
|
35
|
+
* 4. Returns normalized data (with ISO dates) for storage
|
|
32
36
|
*
|
|
33
37
|
* @param schema - The schema type containing field definitions
|
|
34
38
|
* @param data - The document data to validate
|
|
35
39
|
* @param context - Optional context to pass to field validators
|
|
36
|
-
* @returns Validation result with isValid flag and
|
|
40
|
+
* @returns Validation result with isValid flag, errors, and normalized data
|
|
37
41
|
*/
|
|
38
42
|
export declare function validateDocumentData(schema: SchemaType, data: Record<string, any>, context?: any): Promise<DocumentValidationResult>;
|
|
39
43
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/field-validation/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/field-validation/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAIxD,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,wBAAwB;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACnD,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAUrD;AAED;;GAEG;AACH,wBAAsB,aAAa,CAClC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,GAAG,EACV,OAAO,GAAE,GAAQ,GACf,OAAO,CAAC;IACV,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC1B,CAAC,CA+HD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,CAO/D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,oBAAoB,CACzC,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,OAAO,GAAE,GAAQ,GACf,OAAO,CAAC,wBAAwB,CAAC,CAqDnC"}
|