@aphexcms/cms-core 0.1.12 → 0.1.14
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 +15 -12
- 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/ImageField.svelte +22 -13
- package/dist/components/admin/fields/ImageField.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/package.json +101 -95
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Benjamin Sinidol
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/api/documents.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Document, DocumentListParams, CreateDocumentData, UpdateDocumentDa
|
|
|
2
2
|
export declare class DocumentsApi {
|
|
3
3
|
/**
|
|
4
4
|
* List documents with optional filtering
|
|
5
|
+
* NOTE: Requires 'type' parameter - use getByType() for convenience
|
|
5
6
|
*/
|
|
6
7
|
static list(params?: DocumentListParams): Promise<ApiResponse<Document[]>>;
|
|
7
8
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/lib/api/documents.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,QAAQ,EACR,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,EACX,MAAM,SAAS,CAAC;AAEjB,qBAAa,YAAY;IACxB
|
|
1
|
+
{"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/lib/api/documents.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,QAAQ,EACR,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,EACX,MAAM,SAAS,CAAC;AAEjB,qBAAa,YAAY;IACxB;;;OAGG;WACU,IAAI,CAAC,MAAM,GAAE,kBAAuB,GAAG,OAAO,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC;IAYpF;;OAEG;WACU,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IAIhE;;OAEG;WACU,MAAM,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IAI7E;;OAEG;WACU,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IAI7F;;OAEG;WACU,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IAIhE;;OAEG;WACU,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IAIlE;;OAEG;WACU,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAI/D;;OAEG;WACU,SAAS,CACrB,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAM,GAC9C,OAAO,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC;IAInC;;OAEG;WACU,YAAY,CACxB,MAAM,GAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,CAAM,GAC7C,OAAO,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC;IAInC;;OAEG;WACU,SAAS,CACrB,MAAM,GAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,CAAM,GAC7C,OAAO,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC;CAGnC;AAGD,eAAO,MAAM,SAAS;;;;;;;;;;;CAWrB,CAAC"}
|
package/dist/api/documents.js
CHANGED
|
@@ -3,9 +3,17 @@ import { apiClient } from './client.js';
|
|
|
3
3
|
export class DocumentsApi {
|
|
4
4
|
/**
|
|
5
5
|
* List documents with optional filtering
|
|
6
|
+
* NOTE: Requires 'type' parameter - use getByType() for convenience
|
|
6
7
|
*/
|
|
7
8
|
static async list(params = {}) {
|
|
8
|
-
|
|
9
|
+
// Support both 'type' and legacy 'docType' parameter
|
|
10
|
+
const queryParams = {
|
|
11
|
+
...params,
|
|
12
|
+
type: params.type || params.docType
|
|
13
|
+
};
|
|
14
|
+
// Remove docType to avoid confusion
|
|
15
|
+
delete queryParams.docType;
|
|
16
|
+
return apiClient.get('/documents', queryParams);
|
|
9
17
|
}
|
|
10
18
|
/**
|
|
11
19
|
* Get document by ID
|
package/dist/api/types.d.ts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import type { Document, NewDocument } from '../types/index.js';
|
|
2
|
+
export interface PaginationMeta {
|
|
3
|
+
total: number;
|
|
4
|
+
page: number;
|
|
5
|
+
pageSize: number;
|
|
6
|
+
totalPages: number;
|
|
7
|
+
hasNextPage: boolean;
|
|
8
|
+
hasPrevPage: boolean;
|
|
9
|
+
}
|
|
2
10
|
export interface ApiResponse<T> {
|
|
3
11
|
success: boolean;
|
|
4
12
|
data?: T;
|
|
5
13
|
error?: string;
|
|
6
14
|
message?: string;
|
|
15
|
+
pagination?: PaginationMeta;
|
|
7
16
|
meta?: {
|
|
8
17
|
count: number;
|
|
9
18
|
limit: number;
|
|
@@ -12,12 +21,25 @@ export interface ApiResponse<T> {
|
|
|
12
21
|
};
|
|
13
22
|
}
|
|
14
23
|
export interface DocumentListParams {
|
|
24
|
+
type?: string;
|
|
15
25
|
docType?: string;
|
|
16
26
|
status?: string;
|
|
27
|
+
page?: number;
|
|
28
|
+
pageSize?: number;
|
|
17
29
|
limit?: number;
|
|
18
30
|
offset?: number;
|
|
31
|
+
depth?: number;
|
|
32
|
+
sort?: string;
|
|
33
|
+
perspective?: 'draft' | 'published';
|
|
34
|
+
}
|
|
35
|
+
export interface CreateDocumentData {
|
|
36
|
+
type: string;
|
|
37
|
+
data: Record<string, any>;
|
|
38
|
+
publish?: boolean;
|
|
39
|
+
}
|
|
40
|
+
export interface UpdateDocumentData {
|
|
41
|
+
data: Record<string, any>;
|
|
42
|
+
publish?: boolean;
|
|
19
43
|
}
|
|
20
|
-
export type CreateDocumentData = Omit<NewDocument, 'id' | 'createdAt' | 'updatedAt' | 'publishedAt'>;
|
|
21
|
-
export type UpdateDocumentData = Partial<Pick<Document, 'draftData' | 'status'>>;
|
|
22
44
|
export type { Document, NewDocument };
|
|
23
45
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/api/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/api/types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG5D,MAAM,WAAW,WAAW,CAAC,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/api/types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG5D,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;CACrB;AAGD,MAAM,WAAW,WAAW,CAAC,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,UAAU,CAAC,EAAE,cAAc,CAAC;IAE5B,IAAI,CAAC,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAC7B,CAAC;CACF;AAGD,MAAM,WAAW,kBAAkB;IAElC,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,GAAG,WAAW,CAAC;CACpC;AAGD,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAGD,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAGD,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-hooks.d.ts","sourceRoot":"","sources":["../../src/lib/auth/auth-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAQ,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,wBAAsB,cAAc,CACnC,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,SAAS,EACjB,YAAY,EAAE,YAAY,EAC1B,EAAE,EAAE,eAAe,GACjB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"auth-hooks.d.ts","sourceRoot":"","sources":["../../src/lib/auth/auth-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAQ,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,wBAAsB,cAAc,CACnC,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,SAAS,EACjB,YAAY,EAAE,YAAY,EAC1B,EAAE,EAAE,eAAe,GACjB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAoI1B"}
|
package/dist/auth/auth-hooks.js
CHANGED
|
@@ -50,11 +50,17 @@ export async function handleAuthHook(event, config, authProvider, db) {
|
|
|
50
50
|
// Otherwise, try session (for admin UI making API calls)
|
|
51
51
|
auth = await authProvider.getSession(event.request, db);
|
|
52
52
|
}
|
|
53
|
-
//
|
|
53
|
+
// Check if GraphQL plugin is configured
|
|
54
54
|
let graphqlEndpoint;
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
const hasGraphQLPlugin = config.plugins?.some((p) => {
|
|
56
|
+
if (typeof p === 'string')
|
|
57
|
+
return p === '@aphexcms/graphql-plugin';
|
|
58
|
+
if (typeof p === 'object')
|
|
59
|
+
return p.name === '@aphexcms/graphql-plugin';
|
|
60
|
+
return false;
|
|
61
|
+
});
|
|
62
|
+
if (hasGraphQLPlugin) {
|
|
63
|
+
graphqlEndpoint = '/api/graphql'; // Standard GraphQL endpoint
|
|
58
64
|
}
|
|
59
65
|
// Require authentication for protected API routes
|
|
60
66
|
const protectedApiRoutes = [
|
|
@@ -104,5 +110,13 @@ export async function handleAuthHook(event, config, authProvider, db) {
|
|
|
104
110
|
event.locals.auth = auth;
|
|
105
111
|
}
|
|
106
112
|
}
|
|
113
|
+
// 4. All other routes - try to populate auth if session exists (optional auth)
|
|
114
|
+
// This allows public pages to detect if user is logged in (like WordPress admin bar)
|
|
115
|
+
if (!event.locals.auth) {
|
|
116
|
+
const auth = await authProvider.getSession(event.request, db);
|
|
117
|
+
if (auth) {
|
|
118
|
+
event.locals.auth = auth;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
107
121
|
return null; // Tell the main hook to continue
|
|
108
122
|
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { isFieldRequired } from '../lib/field-validation/utils.js';
|
|
2
|
+
/**
|
|
3
|
+
* Map Aphex field types to TypeScript types
|
|
4
|
+
*/
|
|
5
|
+
function mapFieldTypeToTS(field, schemaMap) {
|
|
6
|
+
switch (field.type) {
|
|
7
|
+
case 'string':
|
|
8
|
+
case 'text':
|
|
9
|
+
case 'slug':
|
|
10
|
+
return 'string';
|
|
11
|
+
case 'number':
|
|
12
|
+
return 'number';
|
|
13
|
+
case 'boolean':
|
|
14
|
+
return 'boolean';
|
|
15
|
+
case 'image':
|
|
16
|
+
// Image fields store reference to asset
|
|
17
|
+
return 'string'; // Asset ID
|
|
18
|
+
case 'array': {
|
|
19
|
+
if (!('of' in field) || !field.of || field.of.length === 0) {
|
|
20
|
+
return 'unknown[]';
|
|
21
|
+
}
|
|
22
|
+
// Union of all possible array item types
|
|
23
|
+
const types = field.of
|
|
24
|
+
.map((ref) => {
|
|
25
|
+
const refSchema = schemaMap.get(ref.type);
|
|
26
|
+
if (refSchema && refSchema.type === 'object') {
|
|
27
|
+
return pascalCase(ref.type);
|
|
28
|
+
}
|
|
29
|
+
return 'unknown';
|
|
30
|
+
})
|
|
31
|
+
.filter((t) => t !== 'unknown');
|
|
32
|
+
if (types.length === 0) {
|
|
33
|
+
return 'unknown[]';
|
|
34
|
+
}
|
|
35
|
+
return types.length === 1 ? `${types[0]}[]` : `Array<${types.join(' | ')}>`;
|
|
36
|
+
}
|
|
37
|
+
case 'object': {
|
|
38
|
+
if (!('fields' in field) || !field.fields) {
|
|
39
|
+
return 'Record<string, unknown>';
|
|
40
|
+
}
|
|
41
|
+
// Generate inline interface for object fields
|
|
42
|
+
const props = field.fields
|
|
43
|
+
.map((f) => {
|
|
44
|
+
const tsType = mapFieldTypeToTS(f, schemaMap);
|
|
45
|
+
const optional = isFieldOptional(f) ? '?' : '';
|
|
46
|
+
return ` ${f.name}${optional}: ${tsType};`;
|
|
47
|
+
})
|
|
48
|
+
.join('\n');
|
|
49
|
+
return `{\n${props}\n}`;
|
|
50
|
+
}
|
|
51
|
+
case 'reference': {
|
|
52
|
+
// References store document ID as string
|
|
53
|
+
return 'string';
|
|
54
|
+
}
|
|
55
|
+
default:
|
|
56
|
+
return 'unknown';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Convert kebab-case or snake_case to PascalCase
|
|
61
|
+
*/
|
|
62
|
+
function pascalCase(str) {
|
|
63
|
+
return str
|
|
64
|
+
.split(/[-_]/)
|
|
65
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
66
|
+
.join('');
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Determine if a field is optional based on validation rules
|
|
70
|
+
*/
|
|
71
|
+
function isFieldOptional(field) {
|
|
72
|
+
return !isFieldRequired(field);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Generate TypeScript interface for a schema type
|
|
76
|
+
*/
|
|
77
|
+
function generateInterface(schema, schemaMap) {
|
|
78
|
+
const interfaceName = pascalCase(schema.name);
|
|
79
|
+
const fields = schema.fields
|
|
80
|
+
.map((field) => {
|
|
81
|
+
const tsType = mapFieldTypeToTS(field, schemaMap);
|
|
82
|
+
const optional = isFieldOptional(field) ? '?' : '';
|
|
83
|
+
const comment = field.description ? ` /** ${field.description} */\n` : '';
|
|
84
|
+
return `${comment} ${field.name}${optional}: ${tsType};`;
|
|
85
|
+
})
|
|
86
|
+
.join('\n');
|
|
87
|
+
// Add id and _meta fields for document types
|
|
88
|
+
const isDocument = schema.type === 'document';
|
|
89
|
+
const metadataFields = isDocument
|
|
90
|
+
? ` /** Document ID */
|
|
91
|
+
id: string;
|
|
92
|
+
${fields}
|
|
93
|
+
/** Document metadata */
|
|
94
|
+
_meta?: {
|
|
95
|
+
type: string;
|
|
96
|
+
status: 'draft' | 'published';
|
|
97
|
+
organizationId: string;
|
|
98
|
+
createdAt: Date | null;
|
|
99
|
+
updatedAt: Date | null;
|
|
100
|
+
createdBy?: string;
|
|
101
|
+
updatedBy?: string;
|
|
102
|
+
publishedAt?: Date | null;
|
|
103
|
+
publishedHash?: string | null;
|
|
104
|
+
};`
|
|
105
|
+
: fields;
|
|
106
|
+
return `export interface ${interfaceName} {\n${metadataFields}\n}`;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Generate the Collections interface augmentation
|
|
110
|
+
*/
|
|
111
|
+
function generateCollectionsAugmentation(documentSchemas) {
|
|
112
|
+
const mappings = documentSchemas
|
|
113
|
+
.map((schema) => {
|
|
114
|
+
const interfaceName = pascalCase(schema.name);
|
|
115
|
+
return ` ${schema.name}: CollectionAPI<${interfaceName}>;`;
|
|
116
|
+
})
|
|
117
|
+
.join('\n');
|
|
118
|
+
return `declare module '@aphexcms/cms-core/server' {
|
|
119
|
+
interface Collections {
|
|
120
|
+
${mappings}
|
|
121
|
+
}
|
|
122
|
+
}`;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Generate complete TypeScript types file with module augmentation
|
|
126
|
+
*/
|
|
127
|
+
export function generateTypes(schemas) {
|
|
128
|
+
// Create schema map for lookups
|
|
129
|
+
const schemaMap = new Map(schemas.map((s) => [s.name, s]));
|
|
130
|
+
// Separate document and object types
|
|
131
|
+
const documentSchemas = schemas.filter((s) => s.type === 'document');
|
|
132
|
+
const objectSchemas = schemas.filter((s) => s.type === 'object');
|
|
133
|
+
// Generate interfaces for all schemas
|
|
134
|
+
const objectInterfaces = objectSchemas.map((s) => generateInterface(s, schemaMap)).join('\n\n');
|
|
135
|
+
const documentInterfaces = documentSchemas.map((s) => generateInterface(s, schemaMap)).join('\n\n');
|
|
136
|
+
// Generate Collections interface augmentation
|
|
137
|
+
const collectionsAugmentation = generateCollectionsAugmentation(documentSchemas);
|
|
138
|
+
// Build the complete file
|
|
139
|
+
const output = `/**
|
|
140
|
+
* Generated types for Aphex CMS
|
|
141
|
+
* This file is auto-generated - DO NOT EDIT manually
|
|
142
|
+
*/
|
|
143
|
+
import type { CollectionAPI } from '@aphexcms/cms-core/server';
|
|
144
|
+
|
|
145
|
+
// ============================================================================
|
|
146
|
+
// Object Types (nested in documents)
|
|
147
|
+
// ============================================================================
|
|
148
|
+
|
|
149
|
+
${objectInterfaces}
|
|
150
|
+
|
|
151
|
+
// ============================================================================
|
|
152
|
+
// Document Types (collections)
|
|
153
|
+
// ============================================================================
|
|
154
|
+
|
|
155
|
+
${documentInterfaces}
|
|
156
|
+
|
|
157
|
+
// ============================================================================
|
|
158
|
+
// Module Augmentation - Extends Collections interface globally
|
|
159
|
+
// ============================================================================
|
|
160
|
+
|
|
161
|
+
${collectionsAugmentation}
|
|
162
|
+
`;
|
|
163
|
+
return output;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* CLI helper to generate types from schema file
|
|
167
|
+
*/
|
|
168
|
+
export async function generateTypesFromConfig(schemaPath, outputPath) {
|
|
169
|
+
try {
|
|
170
|
+
// Resolve paths relative to current working directory
|
|
171
|
+
const path = await import('path');
|
|
172
|
+
const { pathToFileURL } = await import('url');
|
|
173
|
+
const fs = await import('fs/promises');
|
|
174
|
+
const absoluteSchemaPath = path.resolve(process.cwd(), schemaPath);
|
|
175
|
+
const absoluteOutputPath = path.resolve(process.cwd(), outputPath);
|
|
176
|
+
// If the schema file is TypeScript, compile it with esbuild first
|
|
177
|
+
let schemaModulePath = absoluteSchemaPath;
|
|
178
|
+
let tempFile = null;
|
|
179
|
+
if (absoluteSchemaPath.endsWith('.ts')) {
|
|
180
|
+
const { build } = await import('esbuild');
|
|
181
|
+
const tempOutFile = path.join(path.dirname(absoluteSchemaPath), '.temp-schema.mjs');
|
|
182
|
+
await build({
|
|
183
|
+
entryPoints: [absoluteSchemaPath],
|
|
184
|
+
bundle: true,
|
|
185
|
+
format: 'esm',
|
|
186
|
+
platform: 'node',
|
|
187
|
+
outfile: tempOutFile,
|
|
188
|
+
external: ['@aphexcms/*']
|
|
189
|
+
});
|
|
190
|
+
schemaModulePath = tempOutFile;
|
|
191
|
+
tempFile = tempOutFile;
|
|
192
|
+
}
|
|
193
|
+
// Dynamic import the schema types
|
|
194
|
+
// Use file:// URL for proper ESM import
|
|
195
|
+
const schemaModule = await import(pathToFileURL(schemaModulePath).href);
|
|
196
|
+
const schemas = schemaModule.schemaTypes || schemaModule.default;
|
|
197
|
+
// Clean up temp file if created
|
|
198
|
+
if (tempFile) {
|
|
199
|
+
try {
|
|
200
|
+
await fs.unlink(tempFile);
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
// Ignore cleanup errors
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (!schemas || !Array.isArray(schemas)) {
|
|
207
|
+
throw new Error('Invalid schema file: expected schemaTypes array export');
|
|
208
|
+
}
|
|
209
|
+
const generatedTypes = generateTypes(schemas);
|
|
210
|
+
// Write to output file
|
|
211
|
+
await fs.writeFile(absoluteOutputPath, generatedTypes, 'utf-8');
|
|
212
|
+
console.log(`✅ Types generated successfully at: ${absoluteOutputPath}`);
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
console.error('❌ Failed to generate types:', error);
|
|
216
|
+
throw error;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Aphex CMS CLI
|
|
4
|
+
* Uses cac for command parsing + clack for interactive prompts
|
|
5
|
+
*/
|
|
6
|
+
import { intro, outro, spinner, text, cancel, isCancel } from '@clack/prompts';
|
|
7
|
+
import { cac } from 'cac';
|
|
8
|
+
import pc from 'picocolors';
|
|
9
|
+
import { generateTypesFromConfig } from './generate-types.js';
|
|
10
|
+
const cli = cac('aphex');
|
|
11
|
+
// Version from package.json
|
|
12
|
+
const version = '0.1.14';
|
|
13
|
+
// ASCII Art Banner
|
|
14
|
+
function printBanner() {
|
|
15
|
+
console.log(pc.cyan(`${pc.bold('⚡ Aphex CMS')}\n${pc.dim('A modern headless CMS')}`));
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* aphex generate:types [schema-path] [output-path]
|
|
19
|
+
* Generate TypeScript types from schema
|
|
20
|
+
*/
|
|
21
|
+
cli
|
|
22
|
+
.command('generate:types [schema-path] [output-path]', 'Generate TypeScript types from schema')
|
|
23
|
+
.action(async (schemaPath, outputPath) => {
|
|
24
|
+
intro(pc.cyan('⚡ Aphex CMS - Type Generator'));
|
|
25
|
+
try {
|
|
26
|
+
// If paths not provided, prompt for them
|
|
27
|
+
if (!schemaPath) {
|
|
28
|
+
const result = await text({
|
|
29
|
+
message: 'Schema file path:',
|
|
30
|
+
placeholder: './src/lib/schemaTypes/index.ts',
|
|
31
|
+
defaultValue: './src/lib/schemaTypes/index.ts'
|
|
32
|
+
});
|
|
33
|
+
if (isCancel(result)) {
|
|
34
|
+
cancel('Operation cancelled.');
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
schemaPath = result;
|
|
38
|
+
}
|
|
39
|
+
if (!outputPath) {
|
|
40
|
+
const result = await text({
|
|
41
|
+
message: 'Output file path:',
|
|
42
|
+
placeholder: './src/lib/generated-types.ts',
|
|
43
|
+
defaultValue: './src/lib/generated-types.ts'
|
|
44
|
+
});
|
|
45
|
+
if (isCancel(result)) {
|
|
46
|
+
cancel('Operation cancelled.');
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
outputPath = result;
|
|
50
|
+
}
|
|
51
|
+
const s = spinner();
|
|
52
|
+
s.start('Generating types...');
|
|
53
|
+
await generateTypesFromConfig(schemaPath, outputPath);
|
|
54
|
+
s.stop(pc.green('✅ Types generated successfully!'));
|
|
55
|
+
outro(pc.dim(`Output: ${pc.cyan(outputPath)}`));
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
cancel(pc.red('Failed to generate types'));
|
|
59
|
+
console.error(error);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
/**
|
|
64
|
+
* aphex help
|
|
65
|
+
*/
|
|
66
|
+
cli.help();
|
|
67
|
+
/**
|
|
68
|
+
* aphex --version
|
|
69
|
+
*/
|
|
70
|
+
cli.version(version);
|
|
71
|
+
/**
|
|
72
|
+
* Default command - show banner and help
|
|
73
|
+
*/
|
|
74
|
+
cli.on('command:*', () => {
|
|
75
|
+
printBanner();
|
|
76
|
+
console.log(pc.red(`Unknown command: ${cli.args.join(' ')}\n`));
|
|
77
|
+
console.log(`Run ${pc.cyan('aphex --help')} to see available commands.`);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
});
|
|
80
|
+
// Parse CLI args
|
|
81
|
+
cli.parse();
|
|
82
|
+
// If no command provided, show banner and help
|
|
83
|
+
if (!process.argv.slice(2).length) {
|
|
84
|
+
printBanner();
|
|
85
|
+
cli.outputHelp();
|
|
86
|
+
}
|
|
@@ -574,29 +574,32 @@
|
|
|
574
574
|
const previewConfig = schema?.preview;
|
|
575
575
|
|
|
576
576
|
documentsList = result.data.map((doc: any) => {
|
|
577
|
-
|
|
577
|
+
// With LocalAPI, data is already flattened at top level (not in draftData)
|
|
578
|
+
// The document itself IS the data, with _meta containing metadata
|
|
578
579
|
|
|
579
580
|
// Use preview config if available
|
|
580
581
|
const title = previewConfig?.select?.title
|
|
581
|
-
?
|
|
582
|
-
:
|
|
582
|
+
? doc[previewConfig.select.title] || `Untitled`
|
|
583
|
+
: doc.title || `Untitled`;
|
|
583
584
|
|
|
584
585
|
const subtitle = previewConfig?.select?.subtitle
|
|
585
|
-
?
|
|
586
|
+
? doc[previewConfig.select.subtitle]
|
|
586
587
|
: undefined;
|
|
587
588
|
|
|
589
|
+
// Metadata is in _meta field (from LocalAPI transformation)
|
|
590
|
+
const meta = doc._meta || {};
|
|
591
|
+
|
|
588
592
|
return {
|
|
589
593
|
id: doc.id,
|
|
590
594
|
title,
|
|
591
595
|
subtitle,
|
|
592
|
-
status:
|
|
593
|
-
publishedAt:
|
|
594
|
-
updatedAt:
|
|
595
|
-
createdAt:
|
|
596
|
-
hasChanges
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
JSON.stringify(doc.draftData) !== JSON.stringify(doc.publishedData)
|
|
596
|
+
status: meta.status || 'draft',
|
|
597
|
+
publishedAt: meta.publishedAt ? new Date(meta.publishedAt) : null,
|
|
598
|
+
updatedAt: meta.updatedAt ? new Date(meta.updatedAt) : null,
|
|
599
|
+
createdAt: meta.createdAt ? new Date(meta.createdAt) : null,
|
|
600
|
+
// hasChanges is tracked via publishedHash comparison
|
|
601
|
+
// If publishedHash is null, it's never been published or has unpublished changes
|
|
602
|
+
hasChanges: meta.status === 'published' && meta.publishedHash === null
|
|
600
603
|
};
|
|
601
604
|
});
|
|
602
605
|
} else {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminApp.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/AdminApp.svelte.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAShD,UAAU,KAAK;IACd,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5E,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACvE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE;QAAE,KAAK,EAAE,WAAW,GAAG,QAAQ,CAAA;KAAE,CAAC;IAC9C,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;
|
|
1
|
+
{"version":3,"file":"AdminApp.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/AdminApp.svelte.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAShD,UAAU,KAAK;IACd,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5E,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACvE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE;QAAE,KAAK,EAAE,WAAW,GAAG,QAAQ,CAAA;KAAE,CAAC;IAC9C,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAm8BF,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
|
|
@@ -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"}
|