@aphexcms/cms-core 0.1.17 → 0.2.2

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 (120) hide show
  1. package/dist/api/client.d.ts.map +1 -1
  2. package/dist/api/client.js +7 -1
  3. package/dist/api/types.d.ts +2 -0
  4. package/dist/api/types.d.ts.map +1 -1
  5. package/dist/cli/generate-types.js +59 -16
  6. package/dist/cli/index.js +1 -1
  7. package/dist/client/index.d.ts +0 -1
  8. package/dist/client/index.d.ts.map +1 -1
  9. package/dist/client/index.js +0 -1
  10. package/dist/components/AdminApp.svelte +278 -45
  11. package/dist/components/AdminApp.svelte.d.ts +2 -0
  12. package/dist/components/AdminApp.svelte.d.ts.map +1 -1
  13. package/dist/components/admin/DocumentEditor.svelte +60 -13
  14. package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
  15. package/dist/components/admin/ObjectModal.svelte +15 -4
  16. package/dist/components/admin/ObjectModal.svelte.d.ts +1 -0
  17. package/dist/components/admin/ObjectModal.svelte.d.ts.map +1 -1
  18. package/dist/components/admin/SchemaField.svelte +64 -5
  19. package/dist/components/admin/SchemaField.svelte.d.ts +1 -0
  20. package/dist/components/admin/SchemaField.svelte.d.ts.map +1 -1
  21. package/dist/components/admin/fields/ArrayField.svelte +72 -17
  22. package/dist/components/admin/fields/ArrayField.svelte.d.ts +1 -0
  23. package/dist/components/admin/fields/ArrayField.svelte.d.ts.map +1 -1
  24. package/dist/components/admin/fields/DateField.svelte +145 -0
  25. package/dist/components/admin/fields/DateField.svelte.d.ts +14 -0
  26. package/dist/components/admin/fields/DateField.svelte.d.ts.map +1 -0
  27. package/dist/components/admin/fields/DateTimeField.svelte +225 -0
  28. package/dist/components/admin/fields/DateTimeField.svelte.d.ts +14 -0
  29. package/dist/components/admin/fields/DateTimeField.svelte.d.ts.map +1 -0
  30. package/dist/components/admin/fields/ImageField.svelte +20 -7
  31. package/dist/components/admin/fields/ImageField.svelte.d.ts +1 -0
  32. package/dist/components/admin/fields/ImageField.svelte.d.ts.map +1 -1
  33. package/dist/components/admin/fields/ReferenceField.svelte +1 -1
  34. package/dist/components/admin/fields/SlugField.svelte +1 -3
  35. package/dist/components/admin/fields/SlugField.svelte.d.ts.map +1 -1
  36. package/dist/components/admin/fields/StringField.svelte +156 -12
  37. package/dist/components/admin/fields/StringField.svelte.d.ts +3 -2
  38. package/dist/components/admin/fields/StringField.svelte.d.ts.map +1 -1
  39. package/dist/components/admin/fields/URLField.svelte +41 -0
  40. package/dist/components/admin/fields/URLField.svelte.d.ts +14 -0
  41. package/dist/components/admin/fields/URLField.svelte.d.ts.map +1 -0
  42. package/dist/components/index.d.ts +0 -1
  43. package/dist/components/index.d.ts.map +1 -1
  44. package/dist/components/index.js +0 -1
  45. package/dist/components/layout/sidebar/AppSidebar.svelte.d.ts +1 -8
  46. package/dist/components/layout/sidebar/AppSidebar.svelte.d.ts.map +1 -1
  47. package/dist/db/interfaces/document.d.ts +0 -2
  48. package/dist/db/interfaces/document.d.ts.map +1 -1
  49. package/dist/db/interfaces/index.d.ts +2 -1
  50. package/dist/db/interfaces/index.d.ts.map +1 -1
  51. package/dist/db/interfaces/user.d.ts +2 -0
  52. package/dist/db/interfaces/user.d.ts.map +1 -1
  53. package/dist/db/utils/reference-resolver.js +1 -1
  54. package/dist/engine.d.ts.map +1 -1
  55. package/dist/engine.js +3 -0
  56. package/dist/field-validation/date-utils.d.ts +30 -0
  57. package/dist/field-validation/date-utils.d.ts.map +1 -0
  58. package/dist/field-validation/date-utils.js +147 -0
  59. package/dist/field-validation/rule.d.ts +4 -0
  60. package/dist/field-validation/rule.d.ts.map +1 -1
  61. package/dist/field-validation/rule.js +170 -4
  62. package/dist/field-validation/utils.d.ts +7 -3
  63. package/dist/field-validation/utils.d.ts.map +1 -1
  64. package/dist/field-validation/utils.js +129 -35
  65. package/dist/hooks.d.ts.map +1 -1
  66. package/dist/hooks.js +38 -21
  67. package/dist/lib/field-validation/date-utils.js +147 -0
  68. package/dist/lib/field-validation/rule.js +170 -4
  69. package/dist/lib/field-validation/utils.js +129 -35
  70. package/dist/local-api/collection-api.d.ts +16 -4
  71. package/dist/local-api/collection-api.d.ts.map +1 -1
  72. package/dist/local-api/collection-api.js +51 -17
  73. package/dist/local-api/index.d.ts +1 -1
  74. package/dist/local-api/index.d.ts.map +1 -1
  75. package/dist/routes/assets-cdn.d.ts.map +1 -1
  76. package/dist/routes/assets-cdn.js +14 -7
  77. package/dist/routes/assets.d.ts.map +1 -1
  78. package/dist/routes/assets.js +6 -1
  79. package/dist/routes/documents-by-id.d.ts.map +1 -1
  80. package/dist/routes/documents-by-id.js +18 -7
  81. package/dist/routes/documents-publish.js +2 -2
  82. package/dist/routes/documents-query.d.ts +3 -1
  83. package/dist/routes/documents-query.d.ts.map +1 -1
  84. package/dist/routes/documents-query.js +6 -2
  85. package/dist/routes/documents.d.ts.map +1 -1
  86. package/dist/routes/documents.js +20 -4
  87. package/dist/routes/index.d.ts +1 -0
  88. package/dist/routes/index.d.ts.map +1 -1
  89. package/dist/routes/index.js +2 -0
  90. package/dist/routes/user-preferences.d.ts +4 -0
  91. package/dist/routes/user-preferences.d.ts.map +1 -0
  92. package/dist/routes/user-preferences.js +77 -0
  93. package/dist/schema-utils/utils.d.ts +4 -0
  94. package/dist/schema-utils/utils.d.ts.map +1 -1
  95. package/dist/schema-utils/utils.js +23 -2
  96. package/dist/schema-utils/validator.d.ts +4 -0
  97. package/dist/schema-utils/validator.d.ts.map +1 -1
  98. package/dist/schema-utils/validator.js +120 -0
  99. package/dist/types/filters.d.ts +13 -0
  100. package/dist/types/filters.d.ts.map +1 -1
  101. package/dist/types/organization.d.ts +3 -0
  102. package/dist/types/organization.d.ts.map +1 -1
  103. package/dist/types/schemas.d.ts +67 -7
  104. package/dist/types/schemas.d.ts.map +1 -1
  105. package/dist/utils/default-orderings.d.ts +10 -0
  106. package/dist/utils/default-orderings.d.ts.map +1 -0
  107. package/dist/utils/default-orderings.js +63 -0
  108. package/dist/utils/field-defaults.d.ts +8 -0
  109. package/dist/utils/field-defaults.d.ts.map +1 -0
  110. package/dist/utils/field-defaults.js +20 -0
  111. package/dist/utils/index.d.ts +1 -0
  112. package/dist/utils/index.d.ts.map +1 -1
  113. package/dist/utils/index.js +1 -0
  114. package/dist/utils/initial-value-helpers.d.ts +50 -0
  115. package/dist/utils/initial-value-helpers.d.ts.map +1 -0
  116. package/dist/utils/initial-value-helpers.js +70 -0
  117. package/package.json +6 -4
  118. package/dist/components/admin/DocumentTypesList.svelte +0 -97
  119. package/dist/components/admin/DocumentTypesList.svelte.d.ts +0 -14
  120. package/dist/components/admin/DocumentTypesList.svelte.d.ts.map +0 -1
@@ -1,4 +1,5 @@
1
1
  import { Rule } from './rule.js';
2
+ import { normalizeDateFields } from './date-utils.js';
2
3
  /**
3
4
  * Check if a field is required based on its validation rules
4
5
  */
@@ -20,39 +21,106 @@ export function isFieldRequired(field) {
20
21
  * Validate a field value against its validation rules
21
22
  */
22
23
  export async function validateField(field, value, context = {}) {
23
- if (!field.validation) {
24
- return { isValid: true, errors: [] };
24
+ console.log(`[validateField] Validating field "${field.name}"`, {
25
+ type: field.type,
26
+ value,
27
+ hasValidation: !!field.validation
28
+ });
29
+ const allErrors = [];
30
+ // Add automatic validation for date/datetime/url fields based on type
31
+ if (field.type === 'date') {
32
+ const dateField = field;
33
+ const dateFormat = dateField.options?.dateFormat || 'YYYY-MM-DD';
34
+ console.log(`[validateField] Adding automatic DATE validation for "${field.name}"`, {
35
+ dateFormat
36
+ });
37
+ const autoRule = new Rule().date(dateFormat);
38
+ const markers = await autoRule.validate(value, {
39
+ path: [field.name],
40
+ ...context
41
+ });
42
+ allErrors.push(...markers.map((marker) => ({
43
+ level: marker.level,
44
+ message: marker.message
45
+ })));
25
46
  }
26
- try {
27
- const validationFunctions = Array.isArray(field.validation)
28
- ? field.validation
29
- : [field.validation];
30
- const allErrors = [];
31
- for (const validationFn of validationFunctions) {
32
- const rule = validationFn(new Rule());
33
- if (!(rule instanceof Rule)) {
34
- console.error(`Validation function for field "${field.name}" did not return a Rule object. Make sure you are chaining validation methods and returning the result.`);
35
- continue;
47
+ else if (field.type === 'datetime') {
48
+ const dateTimeField = field;
49
+ const dateFormat = dateTimeField.options?.dateFormat || 'YYYY-MM-DD';
50
+ const timeFormat = dateTimeField.options?.timeFormat || 'HH:mm';
51
+ console.log(`[validateField] Adding automatic DATETIME validation for "${field.name}"`, {
52
+ dateFormat,
53
+ timeFormat
54
+ });
55
+ const autoRule = new Rule().datetime(dateFormat, timeFormat);
56
+ const markers = await autoRule.validate(value, {
57
+ path: [field.name],
58
+ ...context
59
+ });
60
+ allErrors.push(...markers.map((marker) => ({
61
+ level: marker.level,
62
+ message: marker.message
63
+ })));
64
+ }
65
+ else if (field.type === 'url') {
66
+ // Only add automatic URL validation if there's no custom validation
67
+ // This allows custom validation to specify different options (scheme, allowRelative, relativeOnly)
68
+ if (!field.validation) {
69
+ console.log(`[validateField] Adding automatic URL validation for "${field.name}"`);
70
+ // Automatic URL validation - only validate if there's a value
71
+ if (value && value !== '') {
72
+ const autoRule = new Rule().uri();
73
+ const markers = await autoRule.validate(value, {
74
+ path: [field.name],
75
+ ...context
76
+ });
77
+ allErrors.push(...markers.map((marker) => ({
78
+ level: marker.level,
79
+ message: marker.message
80
+ })));
36
81
  }
37
- const markers = await rule.validate(value, {
38
- path: [field.name],
39
- ...context
40
- });
41
- allErrors.push(...markers.map((marker) => ({
42
- level: marker.level,
43
- message: marker.message
44
- })));
45
82
  }
46
- const isValid = allErrors.filter((e) => e.level === 'error').length === 0;
47
- return { isValid, errors: allErrors };
83
+ else {
84
+ console.log(`[validateField] Skipping automatic URL validation for "${field.name}" (has custom validation)`);
85
+ }
86
+ }
87
+ // Run user-defined validation rules if present
88
+ if (!field.validation) {
89
+ console.log(`[validateField] No custom validation rules for "${field.name}"`);
48
90
  }
49
- catch (error) {
50
- console.error('Validation error:', error);
51
- return {
52
- isValid: false,
53
- errors: [{ level: 'error', message: 'Validation failed' }]
54
- };
91
+ else {
92
+ try {
93
+ const validationFunctions = Array.isArray(field.validation)
94
+ ? field.validation
95
+ : [field.validation];
96
+ console.log(`[validateField] Field "${field.name}" has ${validationFunctions.length} custom validation function(s)`);
97
+ for (const validationFn of validationFunctions) {
98
+ const rule = validationFn(new Rule());
99
+ if (!(rule instanceof Rule)) {
100
+ console.error(`Validation function for field "${field.name}" did not return a Rule object. Make sure you are chaining validation methods and returning the result.`);
101
+ continue;
102
+ }
103
+ const markers = await rule.validate(value, {
104
+ path: [field.name],
105
+ ...context
106
+ });
107
+ allErrors.push(...markers.map((marker) => ({
108
+ level: marker.level,
109
+ message: marker.message
110
+ })));
111
+ }
112
+ }
113
+ catch (error) {
114
+ console.error(`[validateField] Validation error for "${field.name}":`, error);
115
+ allErrors.push({ level: 'error', message: 'Validation failed' });
116
+ }
55
117
  }
118
+ const isValid = allErrors.filter((e) => e.level === 'error').length === 0;
119
+ console.log(`[validateField] Field "${field.name}" validation complete`, {
120
+ isValid,
121
+ errors: allErrors
122
+ });
123
+ return { isValid, errors: allErrors };
56
124
  }
57
125
  /**
58
126
  * Get validation CSS classes for input styling
@@ -66,20 +134,41 @@ export function getValidationClasses(hasErrors) {
66
134
  }
67
135
  /**
68
136
  * Validate an entire document's data against a schema
69
- * This function validates all fields in a schema against the provided data
70
- * and returns any validation errors found.
137
+ * This function:
138
+ * 1. Normalizes date fields (converts user format to ISO for storage)
139
+ * 2. Converts ISO dates to user format for validation
140
+ * 3. Validates all fields and returns errors
141
+ * 4. Returns normalized data (with ISO dates) for storage
71
142
  *
72
143
  * @param schema - The schema type containing field definitions
73
144
  * @param data - The document data to validate
74
145
  * @param context - Optional context to pass to field validators
75
- * @returns Validation result with isValid flag and array of field errors
146
+ * @returns Validation result with isValid flag, errors, and normalized data
76
147
  */
77
148
  export async function validateDocumentData(schema, data, context = {}) {
149
+ console.log('[validateDocumentData] Starting validation', {
150
+ schemaName: schema.name,
151
+ data
152
+ });
78
153
  const validationErrors = [];
79
- // Validate each field in the schema
154
+ // Normalize date fields: convert to ISO for storage, user format for validation
155
+ const { normalizedData, dataForValidation } = normalizeDateFields(data, schema);
156
+ console.log('[validateDocumentData] After normalization', {
157
+ normalizedData,
158
+ dataForValidation
159
+ });
160
+ // Validate each field using the user-formatted data
80
161
  for (const field of schema.fields) {
81
- const value = data[field.name];
82
- const result = await validateField(field, value, { ...context, ...data });
162
+ const value = dataForValidation[field.name];
163
+ console.log(`[validateDocumentData] Validating field "${field.name}"`, {
164
+ type: field.type,
165
+ value
166
+ });
167
+ const result = await validateField(field, value, { ...context, ...dataForValidation });
168
+ console.log(`[validateDocumentData] Field "${field.name}" validation result`, {
169
+ isValid: result.isValid,
170
+ errors: result.errors
171
+ });
83
172
  if (!result.isValid) {
84
173
  const errorMessages = result.errors.filter((e) => e.level === 'error').map((e) => e.message);
85
174
  if (errorMessages.length > 0) {
@@ -90,8 +179,13 @@ export async function validateDocumentData(schema, data, context = {}) {
90
179
  }
91
180
  }
92
181
  }
93
- return {
182
+ console.log('[validateDocumentData] Final result', {
94
183
  isValid: validationErrors.length === 0,
95
184
  errors: validationErrors
185
+ });
186
+ return {
187
+ isValid: validationErrors.length === 0,
188
+ errors: validationErrors,
189
+ normalizedData
96
190
  };
97
191
  }
@@ -4,6 +4,14 @@ import type { Document } from '../types/document.js';
4
4
  import type { LocalAPIContext } from './types.js';
5
5
  import type { SchemaType } from '../types/schemas.js';
6
6
  import { PermissionChecker } from './permissions.js';
7
+ import { type DocumentValidationResult } from '../field-validation/utils.js';
8
+ /**
9
+ * Result from create/update operations that includes validation
10
+ */
11
+ export interface DocumentResult<T> {
12
+ document: T;
13
+ validation: DocumentValidationResult;
14
+ }
7
15
  /**
8
16
  * Collection API - provides type-safe operations for a single collection
9
17
  * Generic type T represents the document type for this collection
@@ -69,7 +77,7 @@ export declare class CollectionAPI<T = Document> {
69
77
  *
70
78
  * @example
71
79
  * ```typescript
72
- * const page = await api.collections.pages.create(
80
+ * const result = await api.collections.pages.create(
73
81
  * { organizationId: 'org_123', user },
74
82
  * {
75
83
  * title: 'New Page',
@@ -77,27 +85,31 @@ export declare class CollectionAPI<T = Document> {
77
85
  * content: []
78
86
  * }
79
87
  * );
88
+ * // result.document - the created document
89
+ * // result.validation - validation results
80
90
  * ```
81
91
  */
82
92
  create(context: LocalAPIContext, data: Omit<T, 'id' | '_meta'>, options?: {
83
93
  publish?: boolean;
84
- }): Promise<T>;
94
+ }): Promise<DocumentResult<T>>;
85
95
  /**
86
96
  * Update an existing document
87
97
  *
88
98
  * @example
89
99
  * ```typescript
90
- * const updated = await api.collections.pages.update(
100
+ * const result = await api.collections.pages.update(
91
101
  * { organizationId: 'org_123', user },
92
102
  * 'doc_123',
93
103
  * { title: 'Updated Title' },
94
104
  * { publish: true }
95
105
  * );
106
+ * // result.document - the updated document
107
+ * // result.validation - validation results
96
108
  * ```
97
109
  */
98
110
  update(context: LocalAPIContext, id: string, data: Partial<Omit<T, 'id' | '_meta'>>, options?: {
99
111
  publish?: boolean;
100
- }): Promise<T | null>;
112
+ }): Promise<DocumentResult<T> | null>;
101
113
  /**
102
114
  * Delete a document
103
115
  *
@@ -1 +1 @@
1
- {"version":3,"file":"collection-api.d.ts","sourceRoot":"","sources":["../../src/lib/local-api/collection-api.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AA6BlD;;;GAGG;AACH,qBAAa,aAAa,CAAC,CAAC,GAAG,QAAQ;IAErC,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,eAAe;IACvB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,WAAW;gBAHX,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,UAAU,EACnB,WAAW,EAAE,iBAAiB;IAMvC;;OAEG;IACH,IAAI,MAAM,IAAI,UAAU,CAEvB;IAED;;;;;;;;;;;;;;;;;OAiBG;IACG,IAAI,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,GAAE,WAAW,CAAC,CAAC,CAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAqB1F;;;;;;;;;;;OAWG;IACG,QAAQ,CACb,OAAO,EAAE,eAAe,EACxB,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAC/B,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAmBpB;;;;;;;;;;OAUG;IACG,KAAK,CACV,OAAO,EAAE,eAAe,EACxB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,KAAK,CAAA;KAAE,GACzC,OAAO,CAAC,MAAM,CAAC;IAWlB;;;;;;;;;;;;;;OAcG;IACG,MAAM,CACX,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAC7B,OAAO,CAAC,CAAC,CAAC;IAoCb;;;;;;;;;;;;OAYG;IACG,MAAM,CACX,OAAO,EAAE,eAAe,EACxB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC,EACtC,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAC7B,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IA0CpB;;;;;;;;;;OAUG;IACG,MAAM,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAOpE;;;;;;;;;;OAUG;IACG,OAAO,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAgCtE;;;;;;;;;;OAUG;IACG,SAAS,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;CAUxE"}
1
+ {"version":3,"file":"collection-api.d.ts","sourceRoot":"","sources":["../../src/lib/local-api/collection-api.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAwB,KAAK,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAEhG;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC;IAChC,QAAQ,EAAE,CAAC,CAAC;IACZ,UAAU,EAAE,wBAAwB,CAAC;CACrC;AA4BD;;;GAGG;AACH,qBAAa,aAAa,CAAC,CAAC,GAAG,QAAQ;IAErC,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,eAAe;IACvB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,WAAW;gBAHX,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,UAAU,EACnB,WAAW,EAAE,iBAAiB;IAMvC;;OAEG;IACH,IAAI,MAAM,IAAI,UAAU,CAEvB;IAED;;;;;;;;;;;;;;;;;OAiBG;IACG,IAAI,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,GAAE,WAAW,CAAC,CAAC,CAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAqB1F;;;;;;;;;;;OAWG;IACG,QAAQ,CACb,OAAO,EAAE,eAAe,EACxB,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAC/B,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAmBpB;;;;;;;;;;OAUG;IACG,KAAK,CACV,OAAO,EAAE,eAAe,EACxB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,KAAK,CAAA;KAAE,GACzC,OAAO,CAAC,MAAM,CAAC;IAWlB;;;;;;;;;;;;;;;;OAgBG;IACG,MAAM,CACX,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAC7B,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;IA4C7B;;;;;;;;;;;;;;OAcG;IACG,MAAM,CACX,OAAO,EAAE,eAAe,EACxB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC,EACtC,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAC7B,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAkEpC;;;;;;;;;;OAUG;IACG,MAAM,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAOpE;;;;;;;;;;OAUG;IACG,OAAO,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAgCtE;;;;;;;;;;OAUG;IACG,SAAS,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;CAUxE"}
@@ -124,7 +124,7 @@ export class CollectionAPI {
124
124
  *
125
125
  * @example
126
126
  * ```typescript
127
- * const page = await api.collections.pages.create(
127
+ * const result = await api.collections.pages.create(
128
128
  * { organizationId: 'org_123', user },
129
129
  * {
130
130
  * title: 'New Page',
@@ -132,15 +132,18 @@ export class CollectionAPI {
132
132
  * content: []
133
133
  * }
134
134
  * );
135
+ * // result.document - the created document
136
+ * // result.validation - validation results
135
137
  * ```
136
138
  */
137
139
  async create(context, data, options) {
138
140
  // Permission check (unless overrideAccess)
139
141
  await this.permissions.canWrite(context, this.collectionName);
140
- // Validate data if publishing immediately
142
+ // Validate and normalize data (dates converted to ISO)
143
+ const validationResult = await validateDocumentData(this._schema, data, data);
141
144
  if (options?.publish) {
142
145
  await this.permissions.canPublish(context, this.collectionName);
143
- const validationResult = await validateDocumentData(this._schema, data, data);
146
+ // Block publish if validation fails
144
147
  if (!validationResult.isValid) {
145
148
  const errorMessage = validationResult.errors
146
149
  .map((e) => `${e.field}: ${e.errors.join(', ')}`)
@@ -148,48 +151,73 @@ export class CollectionAPI {
148
151
  throw new Error(`Cannot publish: validation errors - ${errorMessage}`);
149
152
  }
150
153
  }
151
- // Create document with draft data
154
+ // Create document with normalized data (dates in ISO format)
152
155
  const document = await this.databaseAdapter.createDocument({
153
156
  organizationId: context.organizationId,
154
157
  type: this.collectionName,
155
- draftData: data,
158
+ draftData: validationResult.normalizedData,
156
159
  createdBy: context.user?.id
157
160
  });
158
161
  // Publish immediately if requested (validation already done above)
159
162
  if (options?.publish) {
160
163
  const published = await this.databaseAdapter.publishDoc(context.organizationId, document.id);
161
164
  if (published) {
162
- return transformDocument(published, 'published');
165
+ return {
166
+ document: transformDocument(published, 'published'),
167
+ validation: validationResult
168
+ };
163
169
  }
164
170
  }
165
- return transformDocument(document, 'draft');
171
+ return {
172
+ document: transformDocument(document, 'draft'),
173
+ validation: validationResult
174
+ };
166
175
  }
167
176
  /**
168
177
  * Update an existing document
169
178
  *
170
179
  * @example
171
180
  * ```typescript
172
- * const updated = await api.collections.pages.update(
181
+ * const result = await api.collections.pages.update(
173
182
  * { organizationId: 'org_123', user },
174
183
  * 'doc_123',
175
184
  * { title: 'Updated Title' },
176
185
  * { publish: true }
177
186
  * );
187
+ * // result.document - the updated document
188
+ * // result.validation - validation results
178
189
  * ```
179
190
  */
180
191
  async update(context, id, data, options) {
181
192
  // Permission check (unless overrideAccess)
182
193
  await this.permissions.canWrite(context, this.collectionName);
183
- // Update draft data
184
- const document = await this.databaseAdapter.updateDocDraft(context.organizationId, id, data, context.user?.id);
194
+ // Get existing document to merge with updates
195
+ const existingDoc = await this.databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
196
+ if (!existingDoc) {
197
+ return null;
198
+ }
199
+ // Merge existing data with updates, but only keep fields defined in schema
200
+ // This prevents orphaned fields from persisting when schema changes
201
+ const schemaFieldNames = new Set(this._schema.fields.map((f) => f.name));
202
+ const cleanedExisting = {};
203
+ // Only copy fields from existing doc that are still in the schema
204
+ for (const [key, value] of Object.entries(existingDoc.draftData || {})) {
205
+ if (schemaFieldNames.has(key)) {
206
+ cleanedExisting[key] = value;
207
+ }
208
+ }
209
+ // Merge cleaned existing data with new updates
210
+ const mergedData = { ...cleanedExisting, ...data };
211
+ // Validate and normalize the merged data
212
+ const validationResult = await validateDocumentData(this._schema, mergedData, mergedData);
213
+ // Update draft with normalized data (dates in ISO format)
214
+ const document = await this.databaseAdapter.updateDocDraft(context.organizationId, id, validationResult.normalizedData, context.user?.id);
185
215
  if (!document) {
186
216
  return null;
187
217
  }
188
- // Validate and publish immediately if requested
189
218
  if (options?.publish) {
190
219
  await this.permissions.canPublish(context, this.collectionName);
191
- // Validate the updated draft data
192
- const validationResult = await validateDocumentData(this._schema, document.draftData, document.draftData);
220
+ // Block publish if validation fails
193
221
  if (!validationResult.isValid) {
194
222
  const errorMessage = validationResult.errors
195
223
  .map((e) => `${e.field}: ${e.errors.join(', ')}`)
@@ -198,10 +226,16 @@ export class CollectionAPI {
198
226
  }
199
227
  const published = await this.databaseAdapter.publishDoc(context.organizationId, document.id);
200
228
  if (published) {
201
- return transformDocument(published, 'published');
229
+ return {
230
+ document: transformDocument(published, 'published'),
231
+ validation: validationResult
232
+ };
202
233
  }
203
234
  }
204
- return transformDocument(document, 'draft');
235
+ return {
236
+ document: transformDocument(document, 'draft'),
237
+ validation: validationResult
238
+ };
205
239
  }
206
240
  /**
207
241
  * Delete a document
@@ -234,11 +268,11 @@ export class CollectionAPI {
234
268
  // Permission check (unless overrideAccess)
235
269
  await this.permissions.canPublish(context, this.collectionName);
236
270
  // Get the document to access draft data for validation
237
- const document = await this.databaseAdapter.findByDocId(context.organizationId, id);
271
+ const document = await this.databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
238
272
  if (!document || !document.draftData) {
239
273
  throw new Error('Document not found or has no draft content to publish');
240
274
  }
241
- // Validate draft data before publishing
275
+ // Validate draft data (dates already in ISO, will be converted for validation)
242
276
  const validationResult = await validateDocumentData(this._schema, document.draftData, document.draftData);
243
277
  if (!validationResult.isValid) {
244
278
  const errorMessage = validationResult.errors
@@ -101,7 +101,7 @@ export declare function createLocalAPI(config: CMSConfig, userAdapter: DatabaseA
101
101
  * ```
102
102
  */
103
103
  export declare function getLocalAPI(): LocalAPI;
104
- export { CollectionAPI } from './collection-api.js';
104
+ export { CollectionAPI, type DocumentResult } from './collection-api.js';
105
105
  export { PermissionChecker, PermissionError } from './permissions.js';
106
106
  export type { LocalAPIContext, CreateOptions, UpdateOptions } from './types.js';
107
107
  export { authToContext, requireAuth, systemContext } from './auth-helpers.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/local-api/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAIjD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAE3B,CAAC,cAAc,EAAE,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;CAGjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,QAAQ;IAQnB,OAAO,CAAC,MAAM;IAPR,WAAW,EAAE,WAAW,CAAM;IACrC,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,OAAO,CAA0B;gBAGhC,MAAM,EAAE,SAAS,EACzB,WAAW,EAAE,eAAe,EAC5B,aAAa,CAAC,EAAE,eAAe;IAmBhC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAgC7B;;;OAGG;IACH,OAAO,CAAC,UAAU;IAOlB;;OAEG;IACH,kBAAkB,IAAI,MAAM,EAAE;IAI9B;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIpC;;OAEG;IACH,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;CAGzD;AAKD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAC7B,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,eAAe,EAC5B,aAAa,CAAC,EAAE,eAAe,GAC7B,QAAQ,CAGV;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,IAAI,QAAQ,CAKtC;AAGD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACnE,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/local-api/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAIjD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAE3B,CAAC,cAAc,EAAE,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;CAGjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,QAAQ;IAQnB,OAAO,CAAC,MAAM;IAPR,WAAW,EAAE,WAAW,CAAM;IACrC,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,OAAO,CAA0B;gBAGhC,MAAM,EAAE,SAAS,EACzB,WAAW,EAAE,eAAe,EAC5B,aAAa,CAAC,EAAE,eAAe;IAmBhC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAgC7B;;;OAGG;IACH,OAAO,CAAC,UAAU;IAOlB;;OAEG;IACH,kBAAkB,IAAI,MAAM,EAAE;IAI9B;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIpC;;OAEG;IACH,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;CAGzD;AAKD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAC7B,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,eAAe,EAC5B,aAAa,CAAC,EAAE,eAAe,GAC7B,QAAQ,CAGV;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,IAAI,QAAQ,CAKtC;AAGD,OAAO,EAAE,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACnE,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"assets-cdn.d.ts","sourceRoot":"","sources":["../../src/lib/routes/assets-cdn.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,eAAO,MAAM,GAAG,EAAE,cAsLjB,CAAC"}
1
+ {"version":3,"file":"assets-cdn.d.ts","sourceRoot":"","sources":["../../src/lib/routes/assets-cdn.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,eAAO,MAAM,GAAG,EAAE,cAqMjB,CAAC"}
@@ -97,14 +97,21 @@ export const GET = async ({ params, locals, setHeaders, request }) => {
97
97
  console.warn('[Asset CDN] Private asset accessed without auth - DENIED');
98
98
  return new Response('Unauthorized - This asset is private', { status: 401 });
99
99
  }
100
- // If asset is private, verify org matches
101
- if (isPrivate && organizationId && organizationId !== asset.organizationId) {
102
- console.warn('[Asset CDN] Org mismatch for private asset - FORBIDDEN');
103
- return new Response('Forbidden', { status: 403 });
104
- }
105
- // Log the decision
100
+ // If asset is private, verify user has access to the asset's org
101
+ // This includes exact match OR parent org accessing child org asset (hierarchy)
106
102
  if (isPrivate && organizationId) {
107
- console.log('[Asset CDN] Private asset access ALLOWED - user has auth and org matches');
103
+ let hasAccess = organizationId === asset.organizationId; // Same org
104
+ // If not same org, check if asset's org is a child of user's org (hierarchy)
105
+ if (!hasAccess && databaseAdapter.getChildOrganizations) {
106
+ const childOrgs = await databaseAdapter.getChildOrganizations(organizationId);
107
+ hasAccess = childOrgs.includes(asset.organizationId);
108
+ console.log(`[Asset CDN] Checking hierarchy: user org ${organizationId} has ${childOrgs.length} children, asset org ${asset.organizationId} is child? ${hasAccess}`);
109
+ }
110
+ if (!hasAccess) {
111
+ console.warn(`[Asset CDN] Org ${organizationId} cannot access asset from org ${asset.organizationId} - FORBIDDEN`);
112
+ return new Response('Forbidden', { status: 403 });
113
+ }
114
+ console.log(`[Asset CDN] Private asset access ALLOWED - user org ${organizationId} has access to asset org ${asset.organizationId}`);
108
115
  }
109
116
  else if (!isPrivate) {
110
117
  console.log('[Asset CDN] Public asset access ALLOWED');
@@ -1 +1 @@
1
- {"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/lib/routes/assets.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,eAAO,MAAM,IAAI,EAAE,cAiElB,CAAC;AAEF,eAAO,MAAM,GAAG,EAAE,cA4CjB,CAAC"}
1
+ {"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/lib/routes/assets.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,eAAO,MAAM,IAAI,EAAE,cAuElB,CAAC;AAEF,eAAO,MAAM,GAAG,EAAE,cA4CjB,CAAC"}
@@ -23,8 +23,12 @@ export const POST = async ({ request, locals }) => {
23
23
  // Get field metadata for privacy checking
24
24
  const schemaType = formData.get('schemaType') || undefined;
25
25
  const fieldPath = formData.get('fieldPath') || undefined;
26
+ // Get target organization (document's org takes precedence)
27
+ // This allows assets to belong to the document's org, not the uploader's org
28
+ const targetOrganizationId = formData.get('organizationId') || auth.organizationId;
26
29
  // Create asset upload data
27
30
  const uploadData = {
31
+ organizationId: targetOrganizationId, // Asset belongs to document's org
28
32
  buffer,
29
33
  originalFilename: file.name,
30
34
  mimeType: file.type,
@@ -40,7 +44,8 @@ export const POST = async ({ request, locals }) => {
40
44
  }
41
45
  };
42
46
  // Upload asset using the service
43
- const asset = await assetService.uploadAsset(auth.organizationId, uploadData);
47
+ // The adapter will validate hierarchy access via withOrgContext
48
+ const asset = await assetService.uploadAsset(targetOrganizationId, uploadData);
44
49
  return json({
45
50
  success: true,
46
51
  data: asset
@@ -1 +1 @@
1
- {"version":3,"file":"documents-by-id.d.ts","sourceRoot":"","sources":["../../src/lib/routes/documents-by-id.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAKpD,eAAO,MAAM,GAAG,EAAE,cAuEjB,CAAC;AAGF,eAAO,MAAM,GAAG,EAAE,cAqEjB,CAAC;AAGF,eAAO,MAAM,MAAM,EAAE,cA+DpB,CAAC"}
1
+ {"version":3,"file":"documents-by-id.d.ts","sourceRoot":"","sources":["../../src/lib/routes/documents-by-id.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAMpD,eAAO,MAAM,GAAG,EAAE,cAuEjB,CAAC;AAIF,eAAO,MAAM,GAAG,EAAE,cAkFjB,CAAC;AAGF,eAAO,MAAM,MAAM,EAAE,cA+DpB,CAAC"}
@@ -3,6 +3,7 @@ import { json } from '@sveltejs/kit';
3
3
  import { authToContext } from '../local-api/auth-helpers.js';
4
4
  import { PermissionError } from '../local-api/permissions.js';
5
5
  // GET /api/documents/[id] - Get document by ID
6
+ // TODO ENABLE CHILDREN ORG ACCESS BY DEFAULT - BECAUSE IF A PARENT ORG IS TRYING TO ACCESS A CHILD ORG. It should already have access to said id.
6
7
  export const GET = async ({ params, url, locals }) => {
7
8
  try {
8
9
  const { localAPI, databaseAdapter } = locals.aphexCMS;
@@ -16,7 +17,7 @@ export const GET = async ({ params, url, locals }) => {
16
17
  const depth = depthParam ? Math.max(0, Math.min(parseInt(depthParam), 5)) : 0;
17
18
  const perspective = url.searchParams.get('perspective') || 'draft';
18
19
  // First, fetch document to get its type (need this for collection-specific API)
19
- const rawDoc = await databaseAdapter.findByDocId(context.organizationId, id, 0);
20
+ const rawDoc = await databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
20
21
  if (!rawDoc) {
21
22
  return json({ success: false, error: 'Document not found' }, { status: 404 });
22
23
  }
@@ -59,6 +60,7 @@ export const GET = async ({ params, url, locals }) => {
59
60
  }
60
61
  };
61
62
  // PUT /api/documents/[id] - Update document
63
+ // TODO ENABLE CHILDREN ORG ACCESS BY DEFAULT - BECAUSE IF A PARENT ORG IS TRYING TO ACCESS A CHILD ORG. It should already have access to said id.
62
64
  export const PUT = async ({ params, request, locals }) => {
63
65
  try {
64
66
  const { localAPI, databaseAdapter } = locals.aphexCMS;
@@ -71,7 +73,7 @@ export const PUT = async ({ params, request, locals }) => {
71
73
  const documentData = body.draftData || body.data;
72
74
  const shouldPublish = body.publish || false;
73
75
  // Fetch document to get its type
74
- const rawDoc = await databaseAdapter.findByDocId(context.organizationId, id, 0);
76
+ const rawDoc = await databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
75
77
  if (!rawDoc) {
76
78
  return json({ success: false, error: 'Document not found' }, { status: 404 });
77
79
  }
@@ -84,16 +86,17 @@ export const PUT = async ({ params, request, locals }) => {
84
86
  message: `Collection '${rawDoc.type}' not found`
85
87
  }, { status: 400 });
86
88
  }
87
- // Update via LocalAPI (permission checks happen inside)
88
- const updatedDocument = await collection.update(context, id, documentData, {
89
+ // Update via LocalAPI (permission checks and validation happen inside)
90
+ const result = await collection.update(context, id, documentData, {
89
91
  publish: shouldPublish
90
92
  });
91
- if (!updatedDocument) {
93
+ if (!result) {
92
94
  return json({ success: false, error: 'Document not found' }, { status: 404 });
93
95
  }
94
96
  return json({
95
97
  success: true,
96
- data: updatedDocument
98
+ data: result.document,
99
+ validation: result.validation
97
100
  });
98
101
  }
99
102
  catch (error) {
@@ -105,6 +108,14 @@ export const PUT = async ({ params, request, locals }) => {
105
108
  message: error.message
106
109
  }, { status: 403 });
107
110
  }
111
+ // Validation errors from publish attempts
112
+ if (error instanceof Error && error.message.includes('validation errors')) {
113
+ return json({
114
+ success: false,
115
+ error: 'Validation failed',
116
+ message: error.message
117
+ }, { status: 400 });
118
+ }
108
119
  return json({
109
120
  success: false,
110
121
  error: 'Failed to update document',
@@ -122,7 +133,7 @@ export const DELETE = async ({ params, locals }) => {
122
133
  return json({ success: false, error: 'Document ID is required' }, { status: 400 });
123
134
  }
124
135
  // Fetch document to get its type
125
- const rawDoc = await databaseAdapter.findByDocId(context.organizationId, id, 0);
136
+ const rawDoc = await databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
126
137
  if (!rawDoc) {
127
138
  return json({ success: false, error: 'Document not found' }, { status: 404 });
128
139
  }
@@ -16,7 +16,7 @@ export const POST = async ({ params, locals }) => {
16
16
  }, { status: 400 });
17
17
  }
18
18
  // Fetch document to get its type
19
- const rawDoc = await databaseAdapter.findByDocId(context.organizationId, id, 0);
19
+ const rawDoc = await databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
20
20
  if (!rawDoc) {
21
21
  return json({
22
22
  success: false,
@@ -86,7 +86,7 @@ export const DELETE = async ({ params, locals }) => {
86
86
  }, { status: 400 });
87
87
  }
88
88
  // Fetch document to get its type
89
- const rawDoc = await databaseAdapter.findByDocId(context.organizationId, id, 0);
89
+ const rawDoc = await databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
90
90
  if (!rawDoc) {
91
91
  return json({
92
92
  success: false,
@@ -17,7 +17,9 @@ import type { RequestHandler } from '@sveltejs/kit';
17
17
  * "page": 1,
18
18
  * "sort": ["-publishedAt", "title"],
19
19
  * "depth": 1,
20
- * "perspective": "published"
20
+ * "perspective": "published",
21
+ * "filterOrganizationIds": ["org_child1", "org_child2"],
22
+ * "includeChildOrganizations": false
21
23
  * }
22
24
  */
23
25
  export declare const POST: RequestHandler;
@@ -1 +1 @@
1
- {"version":3,"file":"documents-query.d.ts","sourceRoot":"","sources":["../../src/lib/routes/documents-query.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AASpD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,IAAI,EAAE,cAqFlB,CAAC"}
1
+ {"version":3,"file":"documents-query.d.ts","sourceRoot":"","sources":["../../src/lib/routes/documents-query.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AASpD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,IAAI,EAAE,cAuFlB,CAAC"}
@@ -23,7 +23,9 @@ const DEFAULT_PAGE = 1;
23
23
  * "page": 1,
24
24
  * "sort": ["-publishedAt", "title"],
25
25
  * "depth": 1,
26
- * "perspective": "published"
26
+ * "perspective": "published",
27
+ * "filterOrganizationIds": ["org_child1", "org_child2"],
28
+ * "includeChildOrganizations": false
27
29
  * }
28
30
  */
29
31
  export const POST = async ({ request, locals }) => {
@@ -60,7 +62,9 @@ export const POST = async ({ request, locals }) => {
60
62
  sort: body.sort,
61
63
  depth: body.depth !== undefined ? Math.max(0, Math.min(body.depth, 5)) : 0,
62
64
  select: body.select,
63
- perspective: body.perspective || 'draft'
65
+ perspective: body.perspective || 'draft',
66
+ includeChildOrganizations: body.includeChildOrganizations,
67
+ filterOrganizationIds: body.filterOrganizationIds
64
68
  };
65
69
  // Query via LocalAPI
66
70
  const result = await localAPI.collections[documentType].find(context, findOptions);