@aphexcms/cms-core 0.1.12 → 0.1.13
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/LICENSE +21 -0
- package/dist/api/documents.d.ts +1 -0
- package/dist/api/documents.d.ts.map +1 -1
- package/dist/api/documents.js +9 -1
- package/dist/api/types.d.ts +24 -2
- package/dist/api/types.d.ts.map +1 -1
- package/dist/auth/auth-hooks.d.ts.map +1 -1
- package/dist/auth/auth-hooks.js +18 -4
- package/dist/cli/generate-types.js +218 -0
- package/dist/cli/index.js +86 -0
- package/dist/components/AdminApp.svelte +26 -55
- package/dist/components/AdminApp.svelte.d.ts +3 -5
- package/dist/components/AdminApp.svelte.d.ts.map +1 -1
- package/dist/components/admin/DocumentEditor.svelte +60 -14
- package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/ReferenceField.svelte +2 -3
- package/dist/components/admin/fields/ReferenceField.svelte.d.ts.map +1 -1
- package/dist/components/layout/sidebar/AppSidebar.svelte.d.ts +8 -1
- package/dist/components/layout/sidebar/AppSidebar.svelte.d.ts.map +1 -1
- package/dist/db/interfaces/asset.d.ts +22 -0
- package/dist/db/interfaces/asset.d.ts.map +1 -1
- package/dist/db/interfaces/document.d.ts +25 -0
- package/dist/db/interfaces/document.d.ts.map +1 -1
- package/dist/field-validation/utils.d.ts +19 -1
- package/dist/field-validation/utils.d.ts.map +1 -1
- package/dist/field-validation/utils.js +33 -0
- package/dist/hooks.d.ts +2 -0
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +80 -12
- package/dist/lib/auth/provider.js +1 -0
- package/dist/lib/db/index.js +4 -0
- package/dist/lib/db/interfaces/asset.js +1 -0
- package/dist/lib/db/interfaces/document.js +1 -0
- package/dist/lib/db/interfaces/index.js +1 -0
- package/dist/lib/db/interfaces/organization.js +1 -0
- package/dist/lib/db/interfaces/schema.js +1 -0
- package/dist/lib/db/interfaces/user.js +1 -0
- package/dist/lib/email/index.js +4 -0
- package/dist/lib/email/interfaces/email.js +1 -0
- package/dist/lib/field-validation/rule.js +221 -0
- package/dist/lib/field-validation/utils.js +99 -0
- package/dist/lib/storage/interfaces/index.js +2 -0
- package/dist/lib/storage/interfaces/storage.js +1 -0
- package/dist/lib/types/asset.js +2 -0
- package/dist/lib/types/auth.js +41 -0
- package/dist/lib/types/config.js +1 -0
- package/dist/lib/types/document.js +1 -0
- package/dist/lib/types/filters.js +5 -0
- package/dist/lib/types/index.js +9 -0
- package/dist/lib/types/organization.js +3 -0
- package/dist/lib/types/schemas.js +1 -0
- package/dist/lib/types/sidebar.js +1 -0
- package/dist/lib/types/user.js +1 -0
- package/dist/local-api/auth-helpers.d.ts +65 -0
- package/dist/local-api/auth-helpers.d.ts.map +1 -0
- package/dist/local-api/auth-helpers.js +102 -0
- package/dist/local-api/collection-api.d.ts +138 -0
- package/dist/local-api/collection-api.d.ts.map +1 -0
- package/dist/local-api/collection-api.js +276 -0
- package/dist/local-api/index.d.ts +108 -0
- package/dist/local-api/index.d.ts.map +1 -0
- package/dist/local-api/index.js +157 -0
- package/dist/local-api/permissions.d.ts +45 -0
- package/dist/local-api/permissions.d.ts.map +1 -0
- package/dist/local-api/permissions.js +117 -0
- package/dist/local-api/types.d.ts +65 -0
- package/dist/local-api/types.d.ts.map +1 -0
- package/dist/local-api/types.js +4 -0
- package/dist/routes/documents-by-id.d.ts.map +1 -1
- package/dist/routes/documents-by-id.js +84 -63
- package/dist/routes/documents-publish.d.ts.map +1 -1
- package/dist/routes/documents-publish.js +57 -72
- package/dist/routes/documents-query.d.ts +24 -0
- package/dist/routes/documents-query.d.ts.map +1 -0
- package/dist/routes/documents-query.js +95 -0
- package/dist/routes/documents.d.ts.map +1 -1
- package/dist/routes/documents.js +80 -75
- package/dist/routes/index.d.ts +2 -0
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +2 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -0
- package/dist/types/config.d.ts +18 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/document.d.ts +1 -0
- package/dist/types/document.d.ts.map +1 -1
- package/dist/types/filters.d.ts +173 -0
- package/dist/types/filters.d.ts.map +1 -0
- package/dist/types/filters.js +5 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/schemas.d.ts +0 -5
- package/dist/types/schemas.d.ts.map +1 -1
- package/package.json +101 -95
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
|
|
74
74
|
// Hash-based state tracking
|
|
75
75
|
const hasUnpublishedContent = $derived(
|
|
76
|
-
hasUnpublishedChanges(documentData, fullDocument?.publishedHash || null)
|
|
76
|
+
hasUnpublishedChanges(documentData, fullDocument?._meta?.publishedHash || null)
|
|
77
77
|
);
|
|
78
78
|
const canPublish = $derived(
|
|
79
79
|
hasUnpublishedContent && !saving && documentId && !hasValidationErrors
|
|
@@ -198,15 +198,20 @@
|
|
|
198
198
|
// Store full document for hash comparison
|
|
199
199
|
fullDocument = response.data;
|
|
200
200
|
|
|
201
|
-
//
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
console.log('📄
|
|
201
|
+
// With LocalAPI, data is flattened at top level (not in draftData)
|
|
202
|
+
// Extract all fields except id and _meta
|
|
203
|
+
const { id, _meta, ...data } = response.data;
|
|
204
|
+
console.log('📄 Full response.data:', response.data);
|
|
205
|
+
console.log('📄 Extracted data (after destructuring):', data);
|
|
206
|
+
console.log('📄 Keys in extracted data:', Object.keys(data));
|
|
207
|
+
console.log('📄 Published hash:', _meta?.publishedHash);
|
|
205
208
|
|
|
206
209
|
documentData = { ...data };
|
|
210
|
+
console.log('📄 documentData after assignment:', documentData);
|
|
211
|
+
console.log('📄 Keys in documentData:', Object.keys(documentData));
|
|
207
212
|
hasUnsavedChanges = false; // Just loaded, so no unsaved changes
|
|
208
213
|
} else {
|
|
209
|
-
console.
|
|
214
|
+
console.log('❌ Failed to load document data:', response.error);
|
|
210
215
|
saveError = response.error || 'Failed to load document';
|
|
211
216
|
}
|
|
212
217
|
} catch (err) {
|
|
@@ -249,13 +254,47 @@
|
|
|
249
254
|
});
|
|
250
255
|
}
|
|
251
256
|
|
|
257
|
+
// Check if current data differs from last saved draft
|
|
258
|
+
function hasChangesFromSaved(): boolean {
|
|
259
|
+
if (!fullDocument) return true; // No saved version = has changes
|
|
260
|
+
|
|
261
|
+
// Extract saved draft data from fullDocument (same as we do in loadDocumentData)
|
|
262
|
+
const { id, _meta, ...savedData } = fullDocument;
|
|
263
|
+
|
|
264
|
+
// Compare current documentData with saved data using stable JSON
|
|
265
|
+
const currentJson = JSON.stringify(sortObjectForComparison(documentData));
|
|
266
|
+
const savedJson = JSON.stringify(sortObjectForComparison(savedData));
|
|
267
|
+
|
|
268
|
+
return currentJson !== savedJson;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Helper to recursively sort object keys for stable comparison
|
|
272
|
+
function sortObjectForComparison(item: any): any {
|
|
273
|
+
if (item === null || typeof item !== 'object') return item;
|
|
274
|
+
|
|
275
|
+
if (Array.isArray(item)) {
|
|
276
|
+
return item.map(sortObjectForComparison);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const sortedKeys = Object.keys(item).sort();
|
|
280
|
+
const sortedObj: any = {};
|
|
281
|
+
for (const key of sortedKeys) {
|
|
282
|
+
sortedObj[key] = sortObjectForComparison(item[key]);
|
|
283
|
+
}
|
|
284
|
+
return sortedObj;
|
|
285
|
+
}
|
|
286
|
+
|
|
252
287
|
// Watch for changes to trigger auto-save (debounced)
|
|
253
288
|
$effect(() => {
|
|
254
289
|
const hasContent = hasMeaningfulContent(documentData);
|
|
290
|
+
const hasChanges = hasChangesFromSaved();
|
|
255
291
|
|
|
256
|
-
// Only set hasUnsavedChanges if we actually have meaningful data
|
|
257
|
-
if (hasContent) {
|
|
292
|
+
// Only set hasUnsavedChanges if we actually have meaningful data AND it differs from saved
|
|
293
|
+
if (hasContent && hasChanges) {
|
|
258
294
|
hasUnsavedChanges = true;
|
|
295
|
+
} else if (!hasChanges) {
|
|
296
|
+
// If there are no changes from saved, mark as not having unsaved changes
|
|
297
|
+
hasUnsavedChanges = false;
|
|
259
298
|
}
|
|
260
299
|
|
|
261
300
|
if (autoSaveTimer) {
|
|
@@ -263,12 +302,14 @@
|
|
|
263
302
|
}
|
|
264
303
|
|
|
265
304
|
// Debounced auto-save - waits for 800ms pause in typing (like Notion/modern apps)
|
|
266
|
-
// Only auto-save if there's meaningful content and not in read-only mode
|
|
267
|
-
if (hasContent && schema && !isReadOnly) {
|
|
305
|
+
// Only auto-save if there's meaningful content, data has changed, and not in read-only mode
|
|
306
|
+
if (hasContent && hasChanges && schema && !isReadOnly) {
|
|
268
307
|
autoSaveTimer = setTimeout(() => {
|
|
269
|
-
console.log('🔄 Auto-saving after typing pause...', { documentId });
|
|
308
|
+
console.log('🔄 Auto-saving after typing pause (data changed)...', { documentId });
|
|
270
309
|
saveDocument(true); // auto-save
|
|
271
310
|
}, 1200); // Shorter delay - saves faster but still waits for typing pauses
|
|
311
|
+
} else if (!hasChanges) {
|
|
312
|
+
console.log('⏭️ Skipping auto-save - no changes from saved data');
|
|
272
313
|
}
|
|
273
314
|
|
|
274
315
|
return () => {
|
|
@@ -292,11 +333,11 @@
|
|
|
292
333
|
// Create new document
|
|
293
334
|
console.log('🔄 Creating new document with data:', {
|
|
294
335
|
type: documentType,
|
|
295
|
-
|
|
336
|
+
data: documentData
|
|
296
337
|
});
|
|
297
338
|
response = await documents.create({
|
|
298
339
|
type: documentType,
|
|
299
|
-
|
|
340
|
+
data: documentData
|
|
300
341
|
});
|
|
301
342
|
|
|
302
343
|
console.log('📝 Document creation response:', response);
|
|
@@ -315,8 +356,13 @@
|
|
|
315
356
|
} else if (documentId) {
|
|
316
357
|
// Update existing document
|
|
317
358
|
response = await documents.updateById(documentId, {
|
|
318
|
-
|
|
359
|
+
data: documentData
|
|
319
360
|
});
|
|
361
|
+
|
|
362
|
+
// Update fullDocument with the response to keep saved data in sync
|
|
363
|
+
if (response?.success && response.data) {
|
|
364
|
+
fullDocument = response.data;
|
|
365
|
+
}
|
|
320
366
|
}
|
|
321
367
|
|
|
322
368
|
if (response?.success) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocumentEditor.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/admin/DocumentEditor.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAM1D,UAAU,KAAK;IACd,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;
|
|
1
|
+
{"version":3,"file":"DocumentEditor.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/admin/DocumentEditor.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAM1D,UAAU,KAAK;IACd,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAsvBF,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -129,9 +129,8 @@
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
function getDocumentTitle(doc: any): string {
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
return data.title || data.name || data.heading || 'Untitled';
|
|
132
|
+
// With LocalAPI, data is flattened at top level
|
|
133
|
+
return doc.title || doc.name || doc.heading || 'Untitled';
|
|
135
134
|
}
|
|
136
135
|
|
|
137
136
|
const selectedLabel = $derived(selectedDocument ? getDocumentTitle(selectedDocument) : null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReferenceField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ReferenceField.svelte.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,KAAK,EAAwC,MAAM,wBAAwB,CAAC;AAIzF,UAAU,KAAK;IACd,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzC,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;
|
|
1
|
+
{"version":3,"file":"ReferenceField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ReferenceField.svelte.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,KAAK,EAAwC,MAAM,wBAAwB,CAAC;AAIzF,UAAU,KAAK;IACd,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzC,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAmOF,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
import { Sidebar } from '@aphexcms/ui/shadcn/sidebar';
|
|
2
|
+
import type { SidebarData } from '../../../types/sidebar.js';
|
|
3
|
+
import type { ComponentProps } from 'svelte';
|
|
4
|
+
type Props = ComponentProps<typeof Sidebar> & {
|
|
5
|
+
data: SidebarData;
|
|
6
|
+
onSignOut?: () => void | Promise<void>;
|
|
7
|
+
};
|
|
8
|
+
declare const AppSidebar: import("svelte").Component<Props, {}, "">;
|
|
2
9
|
type AppSidebar = ReturnType<typeof AppSidebar>;
|
|
3
10
|
export default AppSidebar;
|
|
4
11
|
//# sourceMappingURL=AppSidebar.svelte.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppSidebar.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/layout/sidebar/AppSidebar.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AppSidebar.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/layout/sidebar/AppSidebar.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,OAAO,EAKP,MAAM,6BAA6B,CAAC;AAItC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAG5C,KAAK,KAAK,GAAG,cAAc,CAAC,OAAO,OAAO,CAAC,GAAG;IAC7C,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC,CAAC;AAqDH,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Asset } from '../../types/index.js';
|
|
2
|
+
import type { Where, FindOptions, FindResult } from '../../types/filters.js';
|
|
2
3
|
export interface AssetFilters {
|
|
3
4
|
organizationId: string;
|
|
4
5
|
assetType?: 'image' | 'file';
|
|
@@ -47,5 +48,26 @@ export interface AssetAdapter {
|
|
|
47
48
|
countAssets(organizationId: string): Promise<number>;
|
|
48
49
|
countAssetsByType(organizationId: string): Promise<Record<string, number>>;
|
|
49
50
|
getTotalAssetsSize(organizationId: string): Promise<number>;
|
|
51
|
+
/**
|
|
52
|
+
* Find multiple assets with advanced filtering and pagination
|
|
53
|
+
* @param organizationId - Organization ID for multi-tenancy
|
|
54
|
+
* @param options - Advanced filter options (where, limit, offset, sort, etc.)
|
|
55
|
+
* @returns Paginated result with assets and metadata
|
|
56
|
+
*/
|
|
57
|
+
findManyAssetsAdvanced(organizationId: string, options?: FindOptions): Promise<FindResult<Asset>>;
|
|
58
|
+
/**
|
|
59
|
+
* Find a single asset by ID
|
|
60
|
+
* @param organizationId - Organization ID for multi-tenancy
|
|
61
|
+
* @param id - Asset ID
|
|
62
|
+
* @returns Asset or null if not found
|
|
63
|
+
*/
|
|
64
|
+
findAssetByIdAdvanced(organizationId: string, id: string): Promise<Asset | null>;
|
|
65
|
+
/**
|
|
66
|
+
* Count assets matching a where clause
|
|
67
|
+
* @param organizationId - Organization ID for multi-tenancy
|
|
68
|
+
* @param where - Filter conditions
|
|
69
|
+
* @returns Count of matching assets
|
|
70
|
+
*/
|
|
71
|
+
countAssetsAdvanced(organizationId: string, where?: Where): Promise<number>;
|
|
50
72
|
}
|
|
51
73
|
//# sourceMappingURL=asset.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asset.d.ts","sourceRoot":"","sources":["../../../src/lib/db/interfaces/asset.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"asset.d.ts","sourceRoot":"","sources":["../../../src/lib/db/interfaces/asset.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Document } from '../../types/index.js';
|
|
2
|
+
import type { Where, FindOptions, FindResult } from '../../types/filters.js';
|
|
2
3
|
export interface DocumentFilters {
|
|
3
4
|
organizationId: string;
|
|
4
5
|
type?: string;
|
|
@@ -32,5 +33,29 @@ export interface DocumentAdapter {
|
|
|
32
33
|
unpublishDoc(organizationId: string, id: string): Promise<Document | null>;
|
|
33
34
|
countDocsByType(organizationId: string, type: string): Promise<number>;
|
|
34
35
|
getDocCountsByType(organizationId: string): Promise<Record<string, number>>;
|
|
36
|
+
/**
|
|
37
|
+
* Find multiple documents with advanced filtering and pagination
|
|
38
|
+
* @param organizationId - Organization ID for multi-tenancy
|
|
39
|
+
* @param collectionName - Collection/document type to query
|
|
40
|
+
* @param options - Advanced filter options (where, limit, offset, sort, etc.)
|
|
41
|
+
* @returns Paginated result with documents and metadata
|
|
42
|
+
*/
|
|
43
|
+
findManyDocAdvanced(organizationId: string, collectionName: string, options?: FindOptions): Promise<FindResult<Document>>;
|
|
44
|
+
/**
|
|
45
|
+
* Find a single document by ID with advanced options
|
|
46
|
+
* @param organizationId - Organization ID for multi-tenancy
|
|
47
|
+
* @param id - Document ID
|
|
48
|
+
* @param options - Options for depth, select, perspective
|
|
49
|
+
* @returns Document or null if not found
|
|
50
|
+
*/
|
|
51
|
+
findByDocIdAdvanced(organizationId: string, id: string, options?: Partial<FindOptions>): Promise<Document | null>;
|
|
52
|
+
/**
|
|
53
|
+
* Count documents matching a where clause
|
|
54
|
+
* @param organizationId - Organization ID for multi-tenancy
|
|
55
|
+
* @param collectionName - Collection/document type to query
|
|
56
|
+
* @param where - Filter conditions
|
|
57
|
+
* @returns Count of matching documents
|
|
58
|
+
*/
|
|
59
|
+
countDocuments(organizationId: string, collectionName: string, where?: Where): Promise<number>;
|
|
35
60
|
}
|
|
36
61
|
//# sourceMappingURL=document.d.ts.map
|
|
@@ -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;
|
|
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,WAAW,CACV,cAAc,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,gBAAgB,CAAC,GAC/C,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IACvB,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IAC1F,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,CACb,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,EACtB,KAAK,CAAC,EAAE,KAAK,GACX,OAAO,CAAC,MAAM,CAAC,CAAC;CACnB"}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
import type { Field } from '../types/index.js';
|
|
1
|
+
import type { Field, SchemaType } from '../types/index.js';
|
|
2
2
|
export interface ValidationError {
|
|
3
3
|
level: 'error' | 'warning' | 'info';
|
|
4
4
|
message: string;
|
|
5
5
|
}
|
|
6
|
+
export interface DocumentValidationResult {
|
|
7
|
+
isValid: boolean;
|
|
8
|
+
errors: Array<{
|
|
9
|
+
field: string;
|
|
10
|
+
errors: string[];
|
|
11
|
+
}>;
|
|
12
|
+
}
|
|
6
13
|
/**
|
|
7
14
|
* Check if a field is required based on its validation rules
|
|
8
15
|
*/
|
|
@@ -18,4 +25,15 @@ export declare function validateField(field: Field, value: any, context?: any):
|
|
|
18
25
|
* Get validation CSS classes for input styling
|
|
19
26
|
*/
|
|
20
27
|
export declare function getValidationClasses(hasErrors: boolean): string;
|
|
28
|
+
/**
|
|
29
|
+
* Validate an entire document's data against a schema
|
|
30
|
+
* This function validates all fields in a schema against the provided data
|
|
31
|
+
* and returns any validation errors found.
|
|
32
|
+
*
|
|
33
|
+
* @param schema - The schema type containing field definitions
|
|
34
|
+
* @param data - The document data to validate
|
|
35
|
+
* @param context - Optional context to pass to field validators
|
|
36
|
+
* @returns Validation result with isValid flag and array of field errors
|
|
37
|
+
*/
|
|
38
|
+
export declare function validateDocumentData(schema: SchemaType, data: Record<string, any>, context?: any): Promise<DocumentValidationResult>;
|
|
21
39
|
//# 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,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;AAGxD,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;CACnD;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,CA6CD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,CAO/D;AAED;;;;;;;;;GASG;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,CA0BnC"}
|
|
@@ -64,3 +64,36 @@ export function getValidationClasses(hasErrors) {
|
|
|
64
64
|
// No green styling for success - only show red for errors
|
|
65
65
|
return '';
|
|
66
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* 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.
|
|
71
|
+
*
|
|
72
|
+
* @param schema - The schema type containing field definitions
|
|
73
|
+
* @param data - The document data to validate
|
|
74
|
+
* @param context - Optional context to pass to field validators
|
|
75
|
+
* @returns Validation result with isValid flag and array of field errors
|
|
76
|
+
*/
|
|
77
|
+
export async function validateDocumentData(schema, data, context = {}) {
|
|
78
|
+
const validationErrors = [];
|
|
79
|
+
// Validate each field in the schema
|
|
80
|
+
for (const field of schema.fields) {
|
|
81
|
+
const value = data[field.name];
|
|
82
|
+
const result = await validateField(field, value, { ...context, ...data });
|
|
83
|
+
if (!result.isValid) {
|
|
84
|
+
const errorMessages = result.errors
|
|
85
|
+
.filter((e) => e.level === 'error')
|
|
86
|
+
.map((e) => e.message);
|
|
87
|
+
if (errorMessages.length > 0) {
|
|
88
|
+
validationErrors.push({
|
|
89
|
+
field: field.name,
|
|
90
|
+
errors: errorMessages
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
isValid: validationErrors.length === 0,
|
|
97
|
+
errors: validationErrors
|
|
98
|
+
};
|
|
99
|
+
}
|
package/dist/hooks.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { StorageAdapter } from './storage/interfaces/storage.js';
|
|
|
6
6
|
import type { EmailAdapter } from './email/index.js';
|
|
7
7
|
import type { AuthProvider } from './auth/provider.js';
|
|
8
8
|
import { CMSEngine } from './engine.js';
|
|
9
|
+
import { type LocalAPI } from './local-api/index.js';
|
|
9
10
|
export interface CMSInstances {
|
|
10
11
|
config: CMSConfig;
|
|
11
12
|
assetService: AssetService;
|
|
@@ -13,6 +14,7 @@ export interface CMSInstances {
|
|
|
13
14
|
databaseAdapter: DatabaseAdapter;
|
|
14
15
|
emailAdapter?: EmailAdapter | null;
|
|
15
16
|
cmsEngine: CMSEngine;
|
|
17
|
+
localAPI: LocalAPI;
|
|
16
18
|
auth?: AuthProvider;
|
|
17
19
|
pluginRoutes?: Map<string, {
|
|
18
20
|
handler: (event: any) => Promise<Response> | Response;
|
package/dist/hooks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/lib/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/lib/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,SAAS,EAA8B,MAAM,eAAe,CAAC;AAC3E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAIpD,OAAO,EAAa,SAAS,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAGlE,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,EAAE,YAAY,CAAC;IAC3B,cAAc,EAAE,cAAc,CAAC;IAC/B,eAAe,EAAE,eAAe,CAAC;IACjC,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,YAAY,CAAC,EAAE,GAAG,CACjB,MAAM,EACN;QAAE,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAC7E,CAAC;CACF;AA4ED,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAyHvD"}
|
package/dist/hooks.js
CHANGED
|
@@ -2,6 +2,7 @@ import { handleAuthHook } from './auth/auth-hooks.js';
|
|
|
2
2
|
import { createStorageAdapter as createStorageAdapterProvider } from './storage/providers/storage.js';
|
|
3
3
|
import { AssetService as AssetServiceClass } from './services/asset-service.js';
|
|
4
4
|
import { createCMS } from './engine.js';
|
|
5
|
+
import { createLocalAPI } from './local-api/index.js';
|
|
5
6
|
let cmsInstances = null;
|
|
6
7
|
let lastConfigHash = null;
|
|
7
8
|
// Helper to generate a simple hash of schema types for change detection
|
|
@@ -18,6 +19,51 @@ function createDefaultStorageAdapter() {
|
|
|
18
19
|
baseUrl: '' // No direct URL - all access through /assets/{id}/{filename}
|
|
19
20
|
});
|
|
20
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Resolves a plugin configuration to an actual CMSPlugin instance
|
|
24
|
+
* Supports three formats:
|
|
25
|
+
* 1. String: '@aphexcms/graphql-plugin' - dynamically imports default export
|
|
26
|
+
* 2. Object with name/options: { name: '@aphexcms/graphql-plugin', options: {...} }
|
|
27
|
+
* 3. Already instantiated plugin: CMSPlugin object
|
|
28
|
+
*/
|
|
29
|
+
async function resolvePlugin(pluginConfig) {
|
|
30
|
+
// Format 3: Already an instantiated plugin
|
|
31
|
+
if (typeof pluginConfig === 'object' && 'install' in pluginConfig) {
|
|
32
|
+
return pluginConfig;
|
|
33
|
+
}
|
|
34
|
+
// Format 1: String reference
|
|
35
|
+
if (typeof pluginConfig === 'string') {
|
|
36
|
+
try {
|
|
37
|
+
const pluginModule = await import(/* @vite-ignore */ pluginConfig);
|
|
38
|
+
const plugin = pluginModule.default || pluginModule;
|
|
39
|
+
if (typeof plugin === 'function') {
|
|
40
|
+
// If it's a factory function, call it with no options
|
|
41
|
+
return plugin();
|
|
42
|
+
}
|
|
43
|
+
return plugin;
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
throw new Error(`Failed to load plugin "${pluginConfig}". Make sure it's installed: npm install ${pluginConfig}\nError: ${error}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Format 2: Object with name and options
|
|
50
|
+
if (typeof pluginConfig === 'object' && 'name' in pluginConfig) {
|
|
51
|
+
try {
|
|
52
|
+
const pluginModule = await import(/* @vite-ignore */ pluginConfig.name);
|
|
53
|
+
const pluginFactory = pluginModule.default || pluginModule;
|
|
54
|
+
if (typeof pluginFactory === 'function') {
|
|
55
|
+
// Call factory with options
|
|
56
|
+
return pluginFactory(pluginConfig.options || {});
|
|
57
|
+
}
|
|
58
|
+
// If not a factory, return as-is (assuming it's already a plugin)
|
|
59
|
+
return pluginFactory;
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
throw new Error(`Failed to load plugin "${pluginConfig.name}". Make sure it's installed: npm install ${pluginConfig.name}\nError: ${error}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
throw new Error(`Invalid plugin configuration: ${JSON.stringify(pluginConfig)}`);
|
|
66
|
+
}
|
|
21
67
|
export function createCMSHook(config) {
|
|
22
68
|
return async ({ event, resolve }) => {
|
|
23
69
|
// Note: In dev mode, /storage/ might be accessible via Vite dev server
|
|
@@ -33,15 +79,43 @@ export function createCMSHook(config) {
|
|
|
33
79
|
const emailAdapter = config.email ?? null;
|
|
34
80
|
const assetService = new AssetServiceClass(storageAdapter, databaseAdapter);
|
|
35
81
|
const cmsEngine = createCMS(config, databaseAdapter);
|
|
82
|
+
// Initialize Local API (unified operations layer)
|
|
83
|
+
const localAPI = createLocalAPI(config, databaseAdapter);
|
|
36
84
|
await cmsEngine.initialize();
|
|
37
|
-
// Build plugin route map (do this ONCE at startup)
|
|
85
|
+
// Build plugin route map and install plugins (do this ONCE at startup)
|
|
38
86
|
const pluginRoutes = new Map();
|
|
87
|
+
// Resolve and install plugins
|
|
39
88
|
if (config.plugins && config.plugins.length > 0) {
|
|
40
|
-
for (const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
89
|
+
for (const pluginConfig of config.plugins) {
|
|
90
|
+
try {
|
|
91
|
+
// Resolve plugin config to actual CMSPlugin instance (may involve dynamic import)
|
|
92
|
+
const plugin = await resolvePlugin(pluginConfig);
|
|
93
|
+
console.log(`🔌 Loading plugin: ${plugin.name}@${plugin.version}`);
|
|
94
|
+
// Build route map before installation
|
|
95
|
+
if (plugin.routes) {
|
|
96
|
+
for (const [path, handler] of Object.entries(plugin.routes)) {
|
|
97
|
+
pluginRoutes.set(path, { handler, pluginName: plugin.name });
|
|
98
|
+
}
|
|
44
99
|
}
|
|
100
|
+
// Create temporary cmsInstances for installation (will be replaced below)
|
|
101
|
+
const tempInstances = {
|
|
102
|
+
config,
|
|
103
|
+
databaseAdapter: databaseAdapter,
|
|
104
|
+
assetService: assetService,
|
|
105
|
+
storageAdapter: storageAdapter,
|
|
106
|
+
emailAdapter: emailAdapter,
|
|
107
|
+
cmsEngine: cmsEngine,
|
|
108
|
+
localAPI: localAPI,
|
|
109
|
+
auth: config.auth?.provider,
|
|
110
|
+
pluginRoutes
|
|
111
|
+
};
|
|
112
|
+
// Install the plugin
|
|
113
|
+
console.log(`🔌 Installing plugin: ${plugin.name}`);
|
|
114
|
+
await plugin.install(tempInstances);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
console.error(`❌ Failed to load/install plugin:`, error);
|
|
118
|
+
throw error;
|
|
45
119
|
}
|
|
46
120
|
}
|
|
47
121
|
}
|
|
@@ -52,16 +126,10 @@ export function createCMSHook(config) {
|
|
|
52
126
|
storageAdapter: storageAdapter,
|
|
53
127
|
emailAdapter: emailAdapter,
|
|
54
128
|
cmsEngine: cmsEngine,
|
|
129
|
+
localAPI: localAPI,
|
|
55
130
|
auth: config.auth?.provider,
|
|
56
131
|
pluginRoutes
|
|
57
132
|
};
|
|
58
|
-
// Install plugins
|
|
59
|
-
if (config.plugins && config.plugins.length > 0) {
|
|
60
|
-
for (const plugin of config.plugins) {
|
|
61
|
-
console.log(`🔌 Installing plugin: ${plugin.name}`);
|
|
62
|
-
await plugin.install(cmsInstances);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
133
|
lastConfigHash = currentConfigHash;
|
|
66
134
|
}
|
|
67
135
|
else if (configChanged) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|