rimecms 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter-sqlite/generate-schema/templates.server.d.ts +9 -9
- package/dist/adapter-sqlite/generate-schema/templates.server.js +9 -9
- package/dist/adapter-sqlite/paths.server.js +24 -15
- package/dist/adapter-sqlite/rows.server.d.ts +1 -1
- package/dist/core/adapter.d.ts +8 -4
- package/dist/core/boot.server.d.ts +2 -2
- package/dist/core/config/context.server.d.ts +2 -2
- package/dist/core/dev/codegen/routes/common.server.d.ts +2 -0
- package/dist/core/dev/codegen/routes/common.server.js +7 -3
- package/dist/core/dev/codegen/routes/util.server.d.ts +2 -1
- package/dist/core/dev/codegen/routes/util.server.js +4 -1
- package/dist/core/dev/config-memo.server.js +4 -0
- package/dist/core/prototype/collection/operations/duplicate.js +1 -1
- package/dist/core/prototype/collection/thumbnail/hooks/set-document-thumbnail.server.js +1 -1
- package/dist/core/prototype/shared/title/hooks/set-document-title.server.js +1 -1
- package/dist/core/prototype/shared/url/addresses.server.d.ts +10 -0
- package/dist/core/prototype/shared/url/addresses.server.js +17 -17
- package/dist/core/prototype/shared/url/hooks/detach-paths.server.d.ts +3 -2
- package/dist/core/prototype/shared/url/hooks/detach-paths.server.js +9 -8
- package/dist/core/prototype/shared/url/hooks/sync-paths.server.d.ts +3 -2
- package/dist/core/prototype/shared/url/hooks/sync-paths.server.js +5 -4
- package/dist/fields/blocks/component/focus/BlocksFocus.svelte +1 -1
- package/dist/fields/link/index.d.ts +1 -1
- package/dist/fields/relation/component/RelationInline.svelte +1 -1
- package/dist/fields/relation/index.d.ts +1 -41
- package/dist/fields/relation/index.js +0 -67
- package/dist/fields/relation/util.d.ts +69 -0
- package/dist/fields/relation/util.js +103 -0
- package/dist/fields/rich-text/client.d.ts +2 -5
- package/dist/fields/rich-text/client.js +1 -3
- package/dist/fields/rich-text/component/Cell.svelte +1 -1
- package/dist/fields/rich-text/core/features/resource/resource-extension.js +3 -3
- package/dist/fields/rich-text/core/features/resource/resource.svelte +1 -1
- package/dist/fields/rich-text/core/features/upload/upload-extension.js +3 -4
- package/dist/fields/rich-text/core/features/upload/upload.svelte +1 -1
- package/dist/fields/rich-text/index.d.ts +0 -17
- package/dist/fields/rich-text/index.js +0 -44
- package/dist/fields/rich-text/json.d.ts +18 -0
- package/dist/fields/rich-text/json.js +44 -0
- package/dist/index.d.ts +2 -9
- package/dist/index.js +3 -8
- package/dist/{panel/components/sections/live → live}/Consumer.svelte +2 -2
- package/dist/{panel/components/sections/live → live}/Consumer.svelte.d.ts +1 -1
- package/dist/{panel/components/sections/live → live}/LiveEdit.svelte +2 -2
- package/dist/{panel/components/sections/live → live}/Provider.svelte +1 -1
- package/dist/{panel/context/live.svelte.d.ts → live/context.svelte.d.ts} +1 -1
- package/dist/{panel/context/live.svelte.js → live/context.svelte.js} +3 -3
- package/dist/panel/components/sections/document/upload-header/drop-zone/DropZone.svelte.d.ts +1 -1
- package/dist/panel/components/ui/calendar/calendar.svelte.d.ts +1 -1
- package/dist/panel/components/ui/checkbox/checkbox.svelte.d.ts +1 -1
- package/dist/panel/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte.d.ts +1 -1
- package/dist/panel/components/ui/switch/switch.svelte.d.ts +1 -1
- package/dist/panel/context/collection.svelte.d.ts +2 -2
- package/dist/panel/context/config.svelte.d.ts +2 -2
- package/dist/panel/context/documentForm.svelte.js +1 -1
- package/dist/panel/index.d.ts +2 -5
- package/dist/panel/index.js +3 -6
- package/dist/panel/public.d.ts +4 -0
- package/dist/panel/public.js +6 -0
- package/dist/public.d.ts +13 -0
- package/dist/public.js +12 -0
- package/package.json +12 -6
- package/dist/fields/relation/client.d.ts +0 -28
- package/dist/fields/relation/client.js +0 -37
- /package/dist/{panel/util → fields/relation}/populate.d.ts +0 -0
- /package/dist/{panel/util → fields/relation}/populate.js +0 -0
- /package/dist/{panel/components/sections/live → live}/LiveEdit.svelte.d.ts +0 -0
- /package/dist/{panel/components/sections/live → live}/Provider.svelte.d.ts +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { FieldReference } from '../../core/fields/builders/form-field-builder.js';
|
|
2
1
|
import type { ColumnDeclaration, TableDeclaration } from '../../core/adapter.js';
|
|
2
|
+
import type { FieldReference } from '../../core/fields/builders/form-field-builder.js';
|
|
3
3
|
import { type TableName } from '../naming.server.js';
|
|
4
4
|
/**
|
|
5
5
|
* Generates the standard imports needed for Drizzle ORM schema definitions
|
|
@@ -113,10 +113,11 @@ export declare const templateUniqueRequired: (field: {
|
|
|
113
113
|
export declare const templatePathsTable: (owner: TableName) => string;
|
|
114
114
|
/** Template rows Relation */
|
|
115
115
|
/**
|
|
116
|
-
* The whole schema
|
|
116
|
+
* The drizzle relations of the whole schema. A read lists them in its `with` to load related rows
|
|
117
|
+
* in the same query, by the names given here.
|
|
117
118
|
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
119
|
+
* A child table (blocks, tree…) points at its owner through `ownerId`. It gets a relation each way,
|
|
120
|
+
* named after the table on the other end:
|
|
120
121
|
*
|
|
121
122
|
* ```ts
|
|
122
123
|
* export const relations = defineRelations(tables, (r) => ({
|
|
@@ -131,14 +132,13 @@ export declare const templatePathsTable: (owner: TableName) => string;
|
|
|
131
132
|
* }));
|
|
132
133
|
* ```
|
|
133
134
|
*
|
|
134
|
-
* A
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
* A reference the read resolves is the other shape: the owner's row points at its target through
|
|
138
|
-
* the field's own column, one direction, keyed by `joinName(column)`:
|
|
135
|
+
* A reference field that stores another document's id in its own column: `updatedBy` holds a staff id.
|
|
136
|
+
* It gets one relation, from that column to the target, named after the column. Two columns can
|
|
137
|
+
* point at the same table:
|
|
139
138
|
*
|
|
140
139
|
* ```ts
|
|
141
140
|
* pages: {
|
|
141
|
+
* createdBy__$doc: r.one.staff({ from: r.pages.createdBy, to: r.staff.id }),
|
|
142
142
|
* updatedBy__$doc: r.one.staff({ from: r.pages.updatedBy, to: r.staff.id })
|
|
143
143
|
* }
|
|
144
144
|
* ```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { toSnakeCase } from '../../util/string.js';
|
|
2
|
-
import { baseTableName, declaredTableProperty, getSchemaColumnNames, joinName, tableName } from '../naming.server.js';
|
|
3
2
|
import dedent from 'dedent';
|
|
3
|
+
import { baseTableName, declaredTableProperty, getSchemaColumnNames, joinName, tableName } from '../naming.server.js';
|
|
4
4
|
const s = toSnakeCase;
|
|
5
5
|
/**
|
|
6
6
|
* Generates the standard imports needed for Drizzle ORM schema definitions
|
|
@@ -204,10 +204,11 @@ export const ${table} = sqliteTable('${table}', {
|
|
|
204
204
|
};
|
|
205
205
|
/** Template rows Relation */
|
|
206
206
|
/**
|
|
207
|
-
* The whole schema
|
|
207
|
+
* The drizzle relations of the whole schema. A read lists them in its `with` to load related rows
|
|
208
|
+
* in the same query, by the names given here.
|
|
208
209
|
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
210
|
+
* A child table (blocks, tree…) points at its owner through `ownerId`. It gets a relation each way,
|
|
211
|
+
* named after the table on the other end:
|
|
211
212
|
*
|
|
212
213
|
* ```ts
|
|
213
214
|
* export const relations = defineRelations(tables, (r) => ({
|
|
@@ -222,14 +223,13 @@ export const ${table} = sqliteTable('${table}', {
|
|
|
222
223
|
* }));
|
|
223
224
|
* ```
|
|
224
225
|
*
|
|
225
|
-
* A
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
* A reference the read resolves is the other shape: the owner's row points at its target through
|
|
229
|
-
* the field's own column, one direction, keyed by `joinName(column)`:
|
|
226
|
+
* A reference field that stores another document's id in its own column: `updatedBy` holds a staff id.
|
|
227
|
+
* It gets one relation, from that column to the target, named after the column. Two columns can
|
|
228
|
+
* point at the same table:
|
|
230
229
|
*
|
|
231
230
|
* ```ts
|
|
232
231
|
* pages: {
|
|
232
|
+
* createdBy__$doc: r.one.staff({ from: r.pages.createdBy, to: r.staff.id }),
|
|
233
233
|
* updatedBy__$doc: r.one.staff({ from: r.pages.updatedBy, to: r.staff.id })
|
|
234
234
|
* }
|
|
235
235
|
* ```
|
|
@@ -56,28 +56,37 @@ const createPathsHandle = ({ db, tables }) => {
|
|
|
56
56
|
.returning();
|
|
57
57
|
return row;
|
|
58
58
|
},
|
|
59
|
-
// One statement
|
|
60
|
-
|
|
59
|
+
// One statement per locale, in one transaction: each row takes its new parent's path in its
|
|
60
|
+
// locale, and its slug there.
|
|
61
|
+
moveUnder: async ({ slug, ownerId, parentId, slugs }) => {
|
|
61
62
|
const { paths } = tablesOf(slug);
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
return db.transaction(async (tx) => {
|
|
64
|
+
const moved = [];
|
|
65
|
+
for (const [locale, value] of Object.entries(slugs)) {
|
|
66
|
+
const rows = await tx
|
|
67
|
+
.update(paths)
|
|
68
|
+
.set({
|
|
69
|
+
slug: value,
|
|
70
|
+
parentPath: parentId ? parentPathIn(paths, parentId, locale) : null
|
|
71
|
+
})
|
|
72
|
+
.where(and(eq(paths.ownerId, ownerId), eq(paths.locale, locale)))
|
|
73
|
+
.returning();
|
|
74
|
+
moved.push(...rows);
|
|
75
|
+
}
|
|
76
|
+
return rowsOf(moved);
|
|
77
|
+
});
|
|
68
78
|
},
|
|
69
|
-
|
|
79
|
+
children: async ({ slug, ownerId }) => {
|
|
70
80
|
const { paths } = tablesOf(slug);
|
|
71
81
|
const self = alias(paths, 'self');
|
|
72
|
-
const
|
|
73
|
-
.
|
|
74
|
-
.
|
|
82
|
+
const rows = await db
|
|
83
|
+
.selectDistinct({ ownerId: paths.ownerId })
|
|
84
|
+
.from(paths)
|
|
75
85
|
.where(sql `(${paths.locale}, ${paths.parentPath}) IN (${db
|
|
76
86
|
.select({ locale: self.locale, path: self.path })
|
|
77
87
|
.from(self)
|
|
78
|
-
.where(eq(self.ownerId, ownerId))})`)
|
|
79
|
-
|
|
80
|
-
return rowsOf(detached);
|
|
88
|
+
.where(eq(self.ownerId, ownerId))})`);
|
|
89
|
+
return rows.map((row) => row.ownerId);
|
|
81
90
|
},
|
|
82
91
|
// `path/` <= p < `path0` is every path starting with `path/`, read off the unique index:
|
|
83
92
|
// '0' is the character after '/'.
|
package/dist/core/adapter.d.ts
CHANGED
|
@@ -330,17 +330,21 @@ export interface PathsHandle {
|
|
|
330
330
|
locale: string;
|
|
331
331
|
value: string;
|
|
332
332
|
}): Promise<PathRow | undefined>;
|
|
333
|
-
/**
|
|
333
|
+
/**
|
|
334
|
+
* Under another parent, or at the top, with the slug it takes there in each of its locales:
|
|
335
|
+
* `{ en: 'team', fr: 'equipe' }`. Answers the page's rows.
|
|
336
|
+
*/
|
|
334
337
|
moveUnder(args: {
|
|
335
338
|
slug: PrototypeSlug;
|
|
336
339
|
ownerId: string;
|
|
337
340
|
parentId: string | null;
|
|
341
|
+
slugs: Record<string, string>;
|
|
338
342
|
}): Promise<PathRow[]>;
|
|
339
|
-
/**
|
|
340
|
-
|
|
343
|
+
/** The pages directly under this one, in any locale. */
|
|
344
|
+
children(args: {
|
|
341
345
|
slug: PrototypeSlug;
|
|
342
346
|
ownerId: string;
|
|
343
|
-
}): Promise<
|
|
347
|
+
}): Promise<string[]>;
|
|
344
348
|
/** The row at `path` and, with `withDescendants`, every row below it, in one locale. */
|
|
345
349
|
under(args: {
|
|
346
350
|
slug: PrototypeSlug;
|
|
@@ -22,13 +22,13 @@ export declare const bootRime: <const C extends Config>(config: BuildConfig<C>)
|
|
|
22
22
|
} ? { [K in NonNullable<C["areas"]>[number] as K extends {
|
|
23
23
|
slug: infer N;
|
|
24
24
|
} ? N extends string ? N : never : never]: K; } : Record<string, never>;
|
|
25
|
-
readonly prototypes: (import("./config/types.js").
|
|
25
|
+
readonly prototypes: (import("./config/types.js").BuiltArea | import("./config/types.js").BuiltCollection)[];
|
|
26
26
|
getDefaultLocale(): string | undefined;
|
|
27
27
|
getLocalesCodes: () => string[];
|
|
28
28
|
isValidLocale: (locale: string) => boolean;
|
|
29
29
|
getArea: (slug: string) => import("./config/types.js").BuiltArea;
|
|
30
30
|
getCollection: (slug: string) => import("./config/types.js").BuiltCollection;
|
|
31
|
-
getBySlug: (slug: string) => import("./config/types.js").
|
|
31
|
+
getBySlug: (slug: string) => import("./config/types.js").BuiltArea | import("./config/types.js").BuiltCollection;
|
|
32
32
|
isCollection: (slug?: string) => slug is import("./prototype/types.js").CollectionSlug;
|
|
33
33
|
isArea: (slug?: string) => slug is import("./prototype/types.js").AreaSlug;
|
|
34
34
|
getDocumentPrototype(slug: import("./prototype/types.js").PrototypeSlug): "area" | "collection";
|
|
@@ -42,7 +42,7 @@ export declare function createConfigContext<const C extends Config>(config: Buil
|
|
|
42
42
|
/**
|
|
43
43
|
* Gets every built prototype config, whatever its kind
|
|
44
44
|
*/
|
|
45
|
-
readonly prototypes: (import("../../types.js").
|
|
45
|
+
readonly prototypes: (import("../../types.js").BuiltArea | import("../../types.js").BuiltCollection)[];
|
|
46
46
|
/**
|
|
47
47
|
* Gets the default locale from the configuration
|
|
48
48
|
*/
|
|
@@ -66,7 +66,7 @@ export declare function createConfigContext<const C extends Config>(config: Buil
|
|
|
66
66
|
/**
|
|
67
67
|
* Retrieves either an area or collection configuration by its slug
|
|
68
68
|
*/
|
|
69
|
-
getBySlug: (slug: string) => import("../../types.js").
|
|
69
|
+
getBySlug: (slug: string) => import("../../types.js").BuiltArea | import("../../types.js").BuiltCollection;
|
|
70
70
|
/**
|
|
71
71
|
* Checks if a slug represents a collection
|
|
72
72
|
*/
|
|
@@ -33,3 +33,5 @@ declare const paramMatcher: (slugs: string[]) => string;
|
|
|
33
33
|
declare const customRoute: (config: any) => string;
|
|
34
34
|
export declare const commonRoutes: Routes;
|
|
35
35
|
export { customRoute, paramMatcher, prototypeApiServer };
|
|
36
|
+
/** Every fixed route file as the templates write it: a template that changes regenerates them. */
|
|
37
|
+
export declare const templatesSurface: () => string;
|
|
@@ -90,7 +90,7 @@ export const load = async ({ locals }: ServerLoadEvent) => {
|
|
|
90
90
|
*/
|
|
91
91
|
const signInPage = () => `
|
|
92
92
|
<script>
|
|
93
|
-
import { SignIn } from '${PACKAGE_NAME}/panel';
|
|
93
|
+
import { SignIn } from '${PACKAGE_NAME}/panel/public';
|
|
94
94
|
const { data } = $props();
|
|
95
95
|
</script>
|
|
96
96
|
|
|
@@ -113,7 +113,7 @@ export const actions = {
|
|
|
113
113
|
*/
|
|
114
114
|
const forgotPasswordPage = () => `
|
|
115
115
|
<script>
|
|
116
|
-
import { ForgotPassword } from '${PACKAGE_NAME}/panel'
|
|
116
|
+
import { ForgotPassword } from '${PACKAGE_NAME}/panel/public'
|
|
117
117
|
const { data } = $props();
|
|
118
118
|
</script>
|
|
119
119
|
<ForgotPassword {data} />`;
|
|
@@ -132,7 +132,7 @@ export const load = (event: ServerLoadEvent) => event.locals.routes.panel.load.f
|
|
|
132
132
|
*/
|
|
133
133
|
const resetPasswordPage = () => `
|
|
134
134
|
<script>
|
|
135
|
-
import { ResetPassword } from '${PACKAGE_NAME}/panel';
|
|
135
|
+
import { ResetPassword } from '${PACKAGE_NAME}/panel/public';
|
|
136
136
|
const { data } = $props();
|
|
137
137
|
</script>
|
|
138
138
|
|
|
@@ -435,3 +435,7 @@ export const commonRoutes = {
|
|
|
435
435
|
}
|
|
436
436
|
};
|
|
437
437
|
export { customRoute, paramMatcher, prototypeApiServer };
|
|
438
|
+
/** Every fixed route file as the templates write it: a template that changes regenerates them. */
|
|
439
|
+
export const templatesSurface = () => Object.entries(commonRoutes)
|
|
440
|
+
.flatMap(([pattern, files]) => Object.entries(files).map(([type, render]) => `${pattern}/${type}:${render()}`))
|
|
441
|
+
.join('\n');
|
|
@@ -16,7 +16,8 @@ export type Routes = Record<string, RouteDefinition>;
|
|
|
16
16
|
* custom, css written straight from config.panel
|
|
17
17
|
* prototypes the slug lists the [slug=<name>] matchers accept
|
|
18
18
|
* rest one +server.ts per prototype route path, and the methods it exports
|
|
19
|
-
*
|
|
19
|
+
* templates the fixed files as the package's templates write them today
|
|
20
|
+
* version the package the templates ship with
|
|
20
21
|
* ```
|
|
21
22
|
*
|
|
22
23
|
* `PANEL_ROUTE` and `CONFIG_DIR` are read once at process start, so a changed value only takes
|
|
@@ -6,6 +6,7 @@ import { PANEL_ROUTE } from '../../../routes/constants.server.js';
|
|
|
6
6
|
import { slugify } from '../../../../util/string.js';
|
|
7
7
|
import fs from 'fs';
|
|
8
8
|
import path from 'path';
|
|
9
|
+
import { templatesSurface } from './common.server.js';
|
|
9
10
|
/**
|
|
10
11
|
* Whether the generated route tree still matches what the config and the package would produce.
|
|
11
12
|
*
|
|
@@ -17,7 +18,8 @@ import path from 'path';
|
|
|
17
18
|
* custom, css written straight from config.panel
|
|
18
19
|
* prototypes the slug lists the [slug=<name>] matchers accept
|
|
19
20
|
* rest one +server.ts per prototype route path, and the methods it exports
|
|
20
|
-
*
|
|
21
|
+
* templates the fixed files as the package's templates write them today
|
|
22
|
+
* version the package the templates ship with
|
|
21
23
|
* ```
|
|
22
24
|
*
|
|
23
25
|
* `PANEL_ROUTE` and `CONFIG_DIR` are read once at process start, so a changed value only takes
|
|
@@ -39,6 +41,7 @@ export function shouldRegenerateRoutes(config) {
|
|
|
39
41
|
.map((p) => `${p.type}:${p.slug}`)
|
|
40
42
|
.join(',')}
|
|
41
43
|
rest:${restSurface()}
|
|
44
|
+
templates:${templatesSurface()}
|
|
42
45
|
version:${rimeVersion()}
|
|
43
46
|
`;
|
|
44
47
|
if (cache.matches('routes', memo))
|
|
@@ -2,6 +2,7 @@ import { flatten } from 'flat';
|
|
|
2
2
|
import cache from './cache.server.js';
|
|
3
3
|
import { CONFIG_DIR } from './constants.server.js';
|
|
4
4
|
import { restSurface } from './prototype-routes.server.js';
|
|
5
|
+
import { templatesSurface } from './codegen/routes/common.server.js';
|
|
5
6
|
import { rimeVersion } from './version.server.js';
|
|
6
7
|
/**
|
|
7
8
|
* We actually need to serialize config values that will trigger
|
|
@@ -76,6 +77,9 @@ const writeMemo = (config) => {
|
|
|
76
77
|
// `generateRoutes` included, so a route added to a prototype has to move it — a version bump
|
|
77
78
|
// does not, inside this repo, and the config never mentions those paths at all.
|
|
78
79
|
.concat(`REST_SURFACE:${restSurface()}`)
|
|
80
|
+
// Nor are the route templates, which change inside this repo and between two packs of one
|
|
81
|
+
// version: the routes they write must follow.
|
|
82
|
+
.concat(`ROUTE_TEMPLATES:${templatesSurface()}`)
|
|
79
83
|
.join('\n');
|
|
80
84
|
if (cache.matches('config', memoStr))
|
|
81
85
|
return false;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { copyLocales } from '../../../locale/copy.server.js';
|
|
2
|
-
import { isJSONContent, richTextJSONToText } from '../../../../fields/rich-text/
|
|
2
|
+
import { isJSONContent, richTextJSONToText } from '../../../../fields/rich-text/json.js';
|
|
3
3
|
import { getValueAtPath, isObjectLiteral, setValueAtPath } from '../../../../util/object.js';
|
|
4
4
|
// If block is localized should not keep its id so it created a new one
|
|
5
5
|
// If block is not localized than it should keep its id so block is updated
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Hooks } from '../../../../pipeline/define-hook.js';
|
|
2
|
-
import { isRelationResolved } from '../../../../../fields/relation/
|
|
2
|
+
import { isRelationResolved } from '../../../../../fields/relation/util.js';
|
|
3
3
|
import {} from '../../../../../types.js';
|
|
4
4
|
import { getValueAtPath } from '../../../../../util/object.js';
|
|
5
5
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Hooks } from '../../../../pipeline/define-hook.js';
|
|
2
|
-
import { richTextJSONToText } from '../../../../../fields/rich-text/
|
|
2
|
+
import { richTextJSONToText } from '../../../../../fields/rich-text/json.js';
|
|
3
3
|
import { getValueAtPath, isObjectLiteral } from '../../../../../util/object.js';
|
|
4
4
|
/**
|
|
5
5
|
* Puts `title` on the document, read from whichever field `asTitle` resolved to.
|
|
@@ -9,6 +9,16 @@ export declare const addAddresses: (args: {
|
|
|
9
9
|
config: BuiltCollection;
|
|
10
10
|
ids: string[];
|
|
11
11
|
}) => Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Moves a page and its subtree under `parentId`, or to the top when it is null. In each locale the
|
|
14
|
+
* page keeps its slug, or takes the next free one when a new sibling holds it: `team`, `team-2`.
|
|
15
|
+
*/
|
|
16
|
+
export declare const movePage: (args: {
|
|
17
|
+
event: RequestEvent;
|
|
18
|
+
config: BuiltCollection;
|
|
19
|
+
ownerId: string;
|
|
20
|
+
parentId: string | null;
|
|
21
|
+
}) => Promise<any>;
|
|
12
22
|
/**
|
|
13
23
|
* The document with its address as it now is: the read that produced it ran before a create or an
|
|
14
24
|
* update gave it one.
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { walkFields } from '../../../fields/walk.js';
|
|
2
1
|
import { logger } from '../../../logger.server.js';
|
|
3
|
-
import { getValueAtPath } from '../../../../util/object.js';
|
|
4
2
|
import { hasUrl } from './enabled.js';
|
|
5
3
|
import { freeSlug, toSlug } from './format.server.js';
|
|
6
4
|
import { formatRows, readForUrls } from './refresh.server.js';
|
|
@@ -9,20 +7,8 @@ export const addressLocales = (event) => {
|
|
|
9
7
|
const codes = event.locals.rime.config.getLocalesCodes();
|
|
10
8
|
return codes.length ? codes : [''];
|
|
11
9
|
};
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
* otherwise, its id as a last resort.
|
|
15
|
-
*/
|
|
16
|
-
const initialSlug = (config, doc) => {
|
|
17
|
-
for (const { field, path } of walkFields(config.fields)) {
|
|
18
|
-
if (field.type !== 'slug')
|
|
19
|
-
continue;
|
|
20
|
-
const value = toSlug(getValueAtPath(path, doc));
|
|
21
|
-
if (value)
|
|
22
|
-
return value;
|
|
23
|
-
}
|
|
24
|
-
return toSlug(doc.title) || doc.id;
|
|
25
|
-
};
|
|
10
|
+
/** The slug a page starts with: its title, its id as a last resort. */
|
|
11
|
+
const initialSlug = (doc) => toSlug(doc.title) || doc.id;
|
|
26
12
|
/**
|
|
27
13
|
* Gives these pages their address in one locale: a slug free among their siblings, under their
|
|
28
14
|
* parent, then their url.
|
|
@@ -43,7 +29,7 @@ const addAddressesIn = async (args) => {
|
|
|
43
29
|
for (const id of ready) {
|
|
44
30
|
const parentId = parentOf(id);
|
|
45
31
|
const taken = await paths.siblingSlugs({ slug: config.slug, locale, parentId });
|
|
46
|
-
const slug = freeSlug(initialSlug(
|
|
32
|
+
const slug = freeSlug(initialSlug(docs.get(id)), taken);
|
|
47
33
|
inserted.push(...(await paths.insert({
|
|
48
34
|
slug: config.slug,
|
|
49
35
|
ownerId: id,
|
|
@@ -70,6 +56,20 @@ export const addAddresses = async (args) => {
|
|
|
70
56
|
await addAddressesIn({ event, config, ids: idsHere, locale });
|
|
71
57
|
}
|
|
72
58
|
};
|
|
59
|
+
/**
|
|
60
|
+
* Moves a page and its subtree under `parentId`, or to the top when it is null. In each locale the
|
|
61
|
+
* page keeps its slug, or takes the next free one when a new sibling holds it: `team`, `team-2`.
|
|
62
|
+
*/
|
|
63
|
+
export const movePage = async (args) => {
|
|
64
|
+
const { event, config, ownerId, parentId } = args;
|
|
65
|
+
const { paths } = event.locals.rime.adapter;
|
|
66
|
+
const slugs = {};
|
|
67
|
+
for (const row of await paths.get({ slug: config.slug, ownerId })) {
|
|
68
|
+
const taken = await paths.siblingSlugs({ slug: config.slug, locale: row.locale, parentId });
|
|
69
|
+
slugs[row.locale] = freeSlug(row.slug, taken);
|
|
70
|
+
}
|
|
71
|
+
return paths.moveUnder({ slug: config.slug, ownerId, parentId, slugs });
|
|
72
|
+
};
|
|
73
73
|
/**
|
|
74
74
|
* The document with its address as it now is: the read that produced it ran before a create or an
|
|
75
75
|
* update gave it one.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Before a delete, the page's children go to the top
|
|
3
|
-
*
|
|
2
|
+
* Before a delete, the page's children go to the top with their subtrees, as `_parent` does, and
|
|
3
|
+
* get their new urls. A child whose slug a top page holds takes the next free one. The page's own
|
|
4
|
+
* rows go with it by cascade.
|
|
4
5
|
*/
|
|
5
6
|
export declare const detachPaths: import("../../../../pipeline/types.js").Hook<"generic", "delete", "before">;
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { Hooks } from '../../../../pipeline/define-hook.js';
|
|
2
|
+
import { movePage } from '../addresses.server.js';
|
|
2
3
|
import { refreshUrls } from '../refresh.server.js';
|
|
3
4
|
/**
|
|
4
|
-
* Before a delete, the page's children go to the top
|
|
5
|
-
*
|
|
5
|
+
* Before a delete, the page's children go to the top with their subtrees, as `_parent` does, and
|
|
6
|
+
* get their new urls. A child whose slug a top page holds takes the next free one. The page's own
|
|
7
|
+
* rows go with it by cascade.
|
|
6
8
|
*/
|
|
7
9
|
export const detachPaths = Hooks.beforeDelete(async function detachPaths(args) {
|
|
8
10
|
const { event, doc } = args;
|
|
9
11
|
const config = args.config;
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
ownerId:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
await refreshUrls({ event, config, locale: row.locale, path: row.path });
|
|
12
|
+
const { paths } = event.locals.rime.adapter;
|
|
13
|
+
for (const ownerId of await paths.children({ slug: config.slug, ownerId: doc.id })) {
|
|
14
|
+
for (const row of await movePage({ event, config, ownerId, parentId: null })) {
|
|
15
|
+
await refreshUrls({ event, config, locale: row.locale, path: row.path });
|
|
16
|
+
}
|
|
16
17
|
}
|
|
17
18
|
return args;
|
|
18
19
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* After a save. A move carries the page and its subtree under the new parent, in every locale
|
|
3
|
-
*
|
|
2
|
+
* After a save. A move carries the page and its subtree under the new parent, in every locale,
|
|
3
|
+
* with a free slug there. Any other save formats the page's own urls again, since `$url` may read
|
|
4
|
+
* any of its fields.
|
|
4
5
|
*
|
|
5
6
|
* An auto-save moves nothing and changes no url: it is skipped.
|
|
6
7
|
*/
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Hooks } from '../../../../pipeline/define-hook.js';
|
|
2
2
|
import { isNested } from '../../../collection/nested/enabled.js';
|
|
3
|
-
import { addAddresses, withAddress } from '../addresses.server.js';
|
|
3
|
+
import { addAddresses, movePage, withAddress } from '../addresses.server.js';
|
|
4
4
|
import { refreshUrls } from '../refresh.server.js';
|
|
5
5
|
/**
|
|
6
|
-
* After a save. A move carries the page and its subtree under the new parent, in every locale
|
|
7
|
-
*
|
|
6
|
+
* After a save. A move carries the page and its subtree under the new parent, in every locale,
|
|
7
|
+
* with a free slug there. Any other save formats the page's own urls again, since `$url` may read
|
|
8
|
+
* any of its fields.
|
|
8
9
|
*
|
|
9
10
|
* An auto-save moves nothing and changes no url: it is skipped.
|
|
10
11
|
*/
|
|
@@ -18,7 +19,7 @@ export const syncPaths = Hooks.afterUpdate(async function syncPaths(args) {
|
|
|
18
19
|
const wasUnder = context.originalDoc?._parent ?? null;
|
|
19
20
|
const moved = isNested(config) && parentId !== wasUnder;
|
|
20
21
|
const rows = moved
|
|
21
|
-
? await
|
|
22
|
+
? await movePage({ event, config, ownerId: doc.id, parentId })
|
|
22
23
|
: await paths.get({ slug: config.slug, ownerId: doc.id });
|
|
23
24
|
// A page saved before it had an address gets one.
|
|
24
25
|
if (!rows.length) {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import type { DocumentFormContext } from '../../../../panel/context/documentForm.svelte.js';
|
|
11
11
|
import { getLocaleContext } from '../../../../panel/context/locale.svelte.js';
|
|
12
12
|
import { getNavContext } from '../../../../panel/context/nav.svelte.js';
|
|
13
|
-
import { populate } from '
|
|
13
|
+
import { populate } from '../../../relation/populate.js';
|
|
14
14
|
import { capitalize } from '../../../../util/string.js';
|
|
15
15
|
import { ChevronRight, PanelRight, ToyBrick, X } from '@lucide/svelte';
|
|
16
16
|
import { untrack } from 'svelte';
|
|
@@ -56,7 +56,7 @@ export declare class LinkFieldBuilder extends FormFieldBuilder<LinkField> {
|
|
|
56
56
|
root?: boolean | undefined;
|
|
57
57
|
index?: boolean | undefined;
|
|
58
58
|
layout: "compact" | "default";
|
|
59
|
-
types: "
|
|
59
|
+
types: "url"[] | "email"[] | "tel"[] | "anchor"[];
|
|
60
60
|
} & {
|
|
61
61
|
component: import("svelte").Component<any>;
|
|
62
62
|
cell?: import("svelte").Component<{
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { getConfigContext } from '../../../panel/context/config.svelte.js';
|
|
10
10
|
import type { DocumentFormContext } from '../../../panel/context/documentForm.svelte.js';
|
|
11
11
|
import { getLocaleContext } from '../../../panel/context/locale.svelte.js';
|
|
12
|
-
import { populate } from '
|
|
12
|
+
import { populate } from '../populate.js';
|
|
13
13
|
import { uploadFiles, type UploadProgress } from '../../../panel/util/upload-file.js';
|
|
14
14
|
import { Image as ImageIcon } from '@lucide/svelte';
|
|
15
15
|
import type { Snippet } from 'svelte';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { DataType } from '../../core/fields/builders/form-field-builder.js';
|
|
2
2
|
import { FormFieldBuilder } from '../../core/fields/builders/form-field-builder.js';
|
|
3
3
|
import type { CollectionSlug, GenericDoc } from '../../core/prototype/types.js';
|
|
4
|
-
import type { DefaultValueFn, FormField
|
|
4
|
+
import type { DefaultValueFn, FormField } from '../types.js';
|
|
5
5
|
import type { RegisterCollection } from '../../index.js';
|
|
6
6
|
import type { WithOptional } from '../../util/types.js';
|
|
7
7
|
export declare class RelationFieldBuilder<Doc extends GenericDoc = GenericDoc> extends FormFieldBuilder<RelationField<Doc>> {
|
|
@@ -29,46 +29,6 @@ export declare class RelationFieldBuilder<Doc extends GenericDoc = GenericDoc> e
|
|
|
29
29
|
protected generateType(): string;
|
|
30
30
|
}
|
|
31
31
|
export declare const relation: (name: string) => RelationFieldBuilder<GenericDoc>;
|
|
32
|
-
/**
|
|
33
|
-
* Checks if a relation value is resolved (contains the actual referenced document).
|
|
34
|
-
*
|
|
35
|
-
* @example
|
|
36
|
-
* // Returns true for a resolved relation
|
|
37
|
-
* isRelationResolved({ title: 'Home Page', _prototype: 'collection', _type: 'pages' });
|
|
38
|
-
*/
|
|
39
|
-
export declare const isRelationResolved: <T>(value: any) => value is T;
|
|
40
|
-
/**
|
|
41
|
-
* Checks if a relation value is a reference (contains only the relationTo and documentId).
|
|
42
|
-
*
|
|
43
|
-
* @example
|
|
44
|
-
* // Returns true for a relation reference
|
|
45
|
-
* isRelationRef({ relationTo: 'pages', documentId: '123' });
|
|
46
|
-
*/
|
|
47
|
-
export declare const isRelationRef: (value: unknown) => value is RelationRef;
|
|
48
|
-
/**
|
|
49
|
-
* Checks if a relation value is unresolved (contains only reference information).
|
|
50
|
-
*
|
|
51
|
-
* @example
|
|
52
|
-
* // Returns true for an unresolved relation
|
|
53
|
-
* isRelationUnresolved({ relationTo: 'pages', documentId: '123' });
|
|
54
|
-
*/
|
|
55
|
-
export declare const isRelationUnresolved: (value: any) => value is Omit<Relation, "path" | "position" | "ownerId">;
|
|
56
|
-
/**
|
|
57
|
-
* Resolves a relation reference to the actual document it points to.
|
|
58
|
-
*
|
|
59
|
-
* @example
|
|
60
|
-
* // Resolves a relation reference to the actual document
|
|
61
|
-
* const doc = await resolveRelationRef({ relationTo: 'pages', documentId: '123' });
|
|
62
|
-
*/
|
|
63
|
-
export declare function resolveRelationRef<T>(item: T | RelationRef | string): Promise<T>;
|
|
64
|
-
/**
|
|
65
|
-
* Resolves a relation field value to the actual documents it points to.
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* // Resolves a relation value to the actual documents
|
|
69
|
-
* const docs = await resolveRelation([{ relationTo: 'pages', documentId: '123' }]);
|
|
70
|
-
*/
|
|
71
|
-
export declare function resolveRelation<T>(value: RelationValue<T> | null | undefined): Promise<T[] | null | undefined>;
|
|
72
32
|
/****************************************************/
|
|
73
33
|
export type RelationField<Doc extends GenericDoc = GenericDoc> = FormField & {
|
|
74
34
|
type: 'relation';
|