@byline/cli 3.20.4 → 4.0.0
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/README.md +42 -2
- package/dist/cli.js +5 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +1 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +10 -14
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/setup-checks.d.ts +9 -0
- package/dist/commands/setup-checks.d.ts.map +1 -1
- package/dist/commands/setup-checks.js +43 -18
- package/dist/commands/setup-checks.js.map +1 -1
- package/dist/commands/setup.js +1 -1
- package/dist/commands/setup.js.map +1 -1
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +6 -0
- package/dist/context.js.map +1 -1
- package/dist/lib/dependency-version.d.ts +12 -0
- package/dist/lib/dependency-version.d.ts.map +1 -0
- package/dist/lib/dependency-version.js +101 -0
- package/dist/lib/dependency-version.js.map +1 -0
- package/dist/lib/planned-writes.d.ts +8 -0
- package/dist/lib/planned-writes.d.ts.map +1 -0
- package/dist/lib/planned-writes.js +177 -0
- package/dist/lib/planned-writes.js.map +1 -0
- package/dist/lib/planned-writes.test.d.ts +2 -0
- package/dist/lib/planned-writes.test.d.ts.map +1 -0
- package/dist/lib/planned-writes.test.js +113 -0
- package/dist/lib/planned-writes.test.js.map +1 -0
- package/dist/lib/release-policy.d.ts +12 -0
- package/dist/lib/release-policy.d.ts.map +1 -0
- package/dist/lib/release-policy.js +20 -0
- package/dist/lib/release-policy.js.map +1 -0
- package/dist/lib/release-policy.test.d.ts +2 -0
- package/dist/lib/release-policy.test.d.ts.map +1 -0
- package/dist/lib/release-policy.test.js +36 -0
- package/dist/lib/release-policy.test.js.map +1 -0
- package/dist/lib/route-config.d.ts +40 -0
- package/dist/lib/route-config.d.ts.map +1 -0
- package/dist/lib/route-config.js +322 -0
- package/dist/lib/route-config.js.map +1 -0
- package/dist/lib/static-config.d.ts +20 -0
- package/dist/lib/static-config.d.ts.map +1 -0
- package/dist/lib/static-config.js +444 -0
- package/dist/lib/static-config.js.map +1 -0
- package/dist/lib/static-config.test.d.ts +2 -0
- package/dist/lib/static-config.test.d.ts.map +1 -0
- package/dist/lib/static-config.test.js +105 -0
- package/dist/lib/static-config.test.js.map +1 -0
- package/dist/lib/template-hook-boundary.test.node.d.ts +2 -0
- package/dist/lib/template-hook-boundary.test.node.d.ts.map +1 -0
- package/dist/lib/template-hook-boundary.test.node.js +30 -0
- package/dist/lib/template-hook-boundary.test.node.js.map +1 -0
- package/dist/lib/template-path.d.ts +5 -0
- package/dist/lib/template-path.d.ts.map +1 -0
- package/dist/lib/template-path.js +9 -0
- package/dist/lib/template-path.js.map +1 -0
- package/dist/lib/workspace-root.d.ts +28 -0
- package/dist/lib/workspace-root.d.ts.map +1 -0
- package/dist/lib/workspace-root.js +205 -0
- package/dist/lib/workspace-root.js.map +1 -0
- package/dist/manifest/deps.d.ts +1 -1
- package/dist/manifest/deps.d.ts.map +1 -1
- package/dist/manifest/deps.js +18 -8
- package/dist/manifest/deps.js.map +1 -1
- package/dist/phases/deps.d.ts +14 -1
- package/dist/phases/deps.d.ts.map +1 -1
- package/dist/phases/deps.js +312 -137
- package/dist/phases/deps.js.map +1 -1
- package/dist/phases/deps.test.d.ts +2 -0
- package/dist/phases/deps.test.d.ts.map +1 -0
- package/dist/phases/deps.test.js +343 -0
- package/dist/phases/deps.test.js.map +1 -0
- package/dist/phases/preflight.d.ts.map +1 -1
- package/dist/phases/preflight.js +59 -11
- package/dist/phases/preflight.js.map +1 -1
- package/dist/phases/prompts.d.ts +2 -0
- package/dist/phases/prompts.d.ts.map +1 -1
- package/dist/phases/prompts.js +51 -2
- package/dist/phases/prompts.js.map +1 -1
- package/dist/phases/routes-ui.test.d.ts +2 -0
- package/dist/phases/routes-ui.test.d.ts.map +1 -0
- package/dist/phases/routes-ui.test.js +766 -0
- package/dist/phases/routes-ui.test.js.map +1 -0
- package/dist/phases/routes.d.ts +4 -1
- package/dist/phases/routes.d.ts.map +1 -1
- package/dist/phases/routes.js +610 -122
- package/dist/phases/routes.js.map +1 -1
- package/dist/phases/scaffold-smoke.test.d.ts +2 -0
- package/dist/phases/scaffold-smoke.test.d.ts.map +1 -0
- package/dist/phases/scaffold-smoke.test.js +168 -0
- package/dist/phases/scaffold-smoke.test.js.map +1 -0
- package/dist/phases/scaffold.d.ts +4 -1
- package/dist/phases/scaffold.d.ts.map +1 -1
- package/dist/phases/scaffold.js +275 -72
- package/dist/phases/scaffold.js.map +1 -1
- package/dist/phases/scaffold.test.d.ts +2 -0
- package/dist/phases/scaffold.test.d.ts.map +1 -0
- package/dist/phases/scaffold.test.js +178 -0
- package/dist/phases/scaffold.test.js.map +1 -0
- package/dist/phases/ui.d.ts +4 -1
- package/dist/phases/ui.d.ts.map +1 -1
- package/dist/phases/ui.js +51 -59
- package/dist/phases/ui.js.map +1 -1
- package/dist/phases/wire/index.d.ts.map +1 -1
- package/dist/phases/wire/index.js +10 -6
- package/dist/phases/wire/index.js.map +1 -1
- package/dist/phases/wire/shared.d.ts +4 -1
- package/dist/phases/wire/shared.d.ts.map +1 -1
- package/dist/phases/wire/vite-config.d.ts.map +1 -1
- package/dist/phases/wire/vite-config.js +69 -20
- package/dist/phases/wire/vite-config.js.map +1 -1
- package/dist/phases/wire/vite-config.test.d.ts +2 -0
- package/dist/phases/wire/vite-config.test.d.ts.map +1 -0
- package/dist/phases/wire/vite-config.test.js +97 -0
- package/dist/phases/wire/vite-config.test.js.map +1 -0
- package/dist/prompts.d.ts +1 -0
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +7 -2
- package/dist/prompts.js.map +1 -1
- package/dist/runner.js +1 -1
- package/dist/runner.js.map +1 -1
- package/dist/templates/byline/admin.config.ts +8 -7
- package/dist/templates/byline/clients.server.ts +77 -0
- package/dist/templates/byline/collection-types.contract.ts +67 -0
- package/dist/templates/byline/collections/index.ts +17 -0
- package/dist/templates/byline/collections/server-hooks.ts +7 -0
- package/dist/templates/byline/generated/collection-types.ts +11 -0
- package/dist/templates/byline/i18n.ts +3 -3
- package/dist/templates/byline/locales.ts +9 -9
- package/dist/templates/byline/public.ts +12 -0
- package/dist/templates/byline/routes.ts +7 -6
- package/dist/templates/byline/scripts/generate-types.ts +81 -0
- package/dist/templates/byline/server.config.ts +5 -5
- package/dist/templates/byline-examples/admin.config.ts +18 -21
- package/dist/templates/byline-examples/blocks/photo-block.ts +3 -2
- package/dist/templates/byline-examples/clients.server.ts +77 -0
- package/dist/templates/byline-examples/collection-types.contract.ts +67 -0
- package/dist/templates/byline-examples/collections/docs/hooks.ts +30 -20
- package/dist/templates/byline-examples/collections/docs/schema.ts +18 -18
- package/dist/templates/byline-examples/collections/index.ts +22 -0
- package/dist/templates/byline-examples/collections/media/components/media-list-view.tsx +11 -6
- package/dist/templates/byline-examples/collections/media/schema.ts +16 -0
- package/dist/templates/byline-examples/collections/news/schema.ts +17 -3
- package/dist/templates/byline-examples/collections/news-categories/schema.ts +13 -0
- package/dist/templates/byline-examples/collections/pages/schema.ts +13 -0
- package/dist/templates/byline-examples/collections/server-hooks.ts +8 -0
- package/dist/templates/byline-examples/fields/ai-text.ts +1 -1
- package/dist/templates/byline-examples/fields/ai-textarea.ts +1 -1
- package/dist/templates/byline-examples/fields/lexical-richtext-ai.tsx +3 -3
- package/dist/templates/byline-examples/generated/collection-types.ts +139 -0
- package/dist/templates/byline-examples/i18n.ts +3 -3
- package/dist/templates/byline-examples/locales.ts +9 -9
- package/dist/templates/byline-examples/public.ts +12 -0
- package/dist/templates/byline-examples/routes.ts +7 -6
- package/dist/templates/byline-examples/scripts/backfill-version-locales.ts +1 -1
- package/dist/templates/byline-examples/scripts/generate-types.ts +81 -0
- package/dist/templates/byline-examples/scripts/import-docs.ts +11 -12
- package/dist/templates/byline-examples/scripts/lib/mdast-to-lexical.test.node.ts +93 -233
- package/dist/templates/byline-examples/scripts/lib/mdast-to-lexical.ts +2 -2
- package/dist/templates/byline-examples/scripts/lib/strip-leading-h1.test.node.ts +0 -4
- package/dist/templates/byline-examples/scripts/re-anchor.ts +3 -3
- package/dist/templates/byline-examples/scripts/regenerate-media.ts +6 -5
- package/dist/templates/byline-examples/server.config.ts +6 -15
- package/dist/templates/host/vite.config.ts +2 -0
- package/dist/templates/routes/_byline/admin/collections/$collection/$id/api.tsx +1 -6
- package/dist/templates/routes/_byline/admin/collections/$collection/$id/history.tsx +1 -7
- package/dist/templates/routes/_byline/admin/collections/$collection/$id/index.tsx +1 -6
- package/dist/templates/routes/_byline/route.lazy.tsx +1 -2
- package/dist/templates/routes/_byline/route.tsx +1 -1
- package/dist/templates/ui-byline/blocks/photo-block/index.tsx +18 -23
- package/dist/templates/ui-byline/blocks/richtext-block/index.tsx +1 -1
- package/dist/templates/ui-byline/components/admonition/index.tsx +11 -8
- package/dist/templates/ui-byline/components/code/code.tsx +6 -4
- package/dist/templates/ui-byline/components/link/lang-link.tsx +4 -8
- package/dist/templates/ui-byline/components/link/link-field.tsx +3 -2
- package/dist/templates/ui-byline/components/link/link-lexical.tsx +9 -8
- package/dist/templates/ui-byline/render-blocks.tsx +38 -38
- package/dist/templates/ui-byline/types/content.ts +25 -0
- package/dist/templates/ui-byline/types/i18n.ts +4 -8
- package/dist/test-helpers.d.ts +5 -0
- package/dist/test-helpers.d.ts.map +1 -0
- package/dist/test-helpers.js +52 -0
- package/dist/test-helpers.js.map +1 -0
- package/dist/types.d.ts +21 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/ui-template-contract.test.d.ts +2 -0
- package/dist/ui-template-contract.test.d.ts.map +1 -0
- package/dist/ui-template-contract.test.js +130 -0
- package/dist/ui-template-contract.test.js.map +1 -0
- package/dist/workspace-root.test.d.ts +2 -0
- package/dist/workspace-root.test.d.ts.map +1 -0
- package/dist/workspace-root.test.js +307 -0
- package/dist/workspace-root.test.js.map +1 -0
- package/package.json +9 -1
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile-time contract between this application's collection schemas and its
|
|
3
|
+
* generated collection-type projection.
|
|
4
|
+
*
|
|
5
|
+
* ```text
|
|
6
|
+
* collections/index.ts generated/collection-types.ts
|
|
7
|
+
* `typeof collections` `CollectionFields*ByPath`
|
|
8
|
+
* | |
|
|
9
|
+
* v v
|
|
10
|
+
* inferred registry ----------- Exact -------- generated registry
|
|
11
|
+
* |
|
|
12
|
+
* v
|
|
13
|
+
* TypeScript error on drift
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* The generated file is deliberately a standalone projection for typed clients
|
|
17
|
+
* and frontend code; it does not import the runtime schemas. This module joins
|
|
18
|
+
* those two app-owned sources at compile time and requires their collection
|
|
19
|
+
* keys, ordinary field shapes, and all-locale field shapes to match exactly.
|
|
20
|
+
*
|
|
21
|
+
* This file has no runtime behavior. Keep it application-owned: `@byline/core`
|
|
22
|
+
* provides the inference and code-generation machinery, but only this
|
|
23
|
+
* application knows its concrete collection tuple and generated output.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import type { CollectionFieldData, CollectionFieldDataAllLocales } from '@byline/core'
|
|
27
|
+
|
|
28
|
+
import type { collections } from './collections/index.js'
|
|
29
|
+
import type {
|
|
30
|
+
CollectionFieldsAllLocalesByPath,
|
|
31
|
+
CollectionFieldsByPath,
|
|
32
|
+
} from './generated/collection-types.js'
|
|
33
|
+
|
|
34
|
+
type InferredFieldsByPath = {
|
|
35
|
+
[Definition in (typeof collections)[number] as Definition['path']]: CollectionFieldData<Definition>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type InferredFieldsAllLocalesByPath = {
|
|
39
|
+
[Definition in (typeof collections)[number] as Definition['path']]: CollectionFieldDataAllLocales<Definition>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type IsAny<Value> = 0 extends 1 & Value ? true : false
|
|
43
|
+
type Exact<Left, Right> = IsAny<Left> extends true
|
|
44
|
+
? IsAny<Right>
|
|
45
|
+
: IsAny<Right> extends true
|
|
46
|
+
? false
|
|
47
|
+
: (<Value>() => Value extends Left ? 1 : 2) extends <Value>() =>
|
|
48
|
+
Value extends Right ? 1 : 2
|
|
49
|
+
? (<Value>() => Value extends Right ? 1 : 2) extends <Value>() =>
|
|
50
|
+
Value extends Left ? 1 : 2
|
|
51
|
+
? true
|
|
52
|
+
: false
|
|
53
|
+
: false
|
|
54
|
+
type Assert<Value extends true> = Value
|
|
55
|
+
|
|
56
|
+
type GeneratedKeysMatchRuntimeRegistry = Assert<
|
|
57
|
+
Exact<keyof CollectionFieldsByPath, keyof InferredFieldsByPath>
|
|
58
|
+
>
|
|
59
|
+
type GeneratedFieldsMatchRuntimeRegistry = Assert<
|
|
60
|
+
Exact<CollectionFieldsByPath, InferredFieldsByPath>
|
|
61
|
+
>
|
|
62
|
+
type GeneratedAllLocalesKeysMatchRuntimeRegistry = Assert<
|
|
63
|
+
Exact<keyof CollectionFieldsAllLocalesByPath, keyof InferredFieldsAllLocalesByPath>
|
|
64
|
+
>
|
|
65
|
+
type GeneratedAllLocalesFieldsMatchRuntimeRegistry = Assert<
|
|
66
|
+
Exact<CollectionFieldsAllLocalesByPath, InferredFieldsAllLocalesByPath>
|
|
67
|
+
>
|
|
@@ -17,22 +17,20 @@
|
|
|
17
17
|
* config from it. Anything the schema *statically imports* therefore ships to
|
|
18
18
|
* the client. Hook *bodies*, however, only ever run server-side.
|
|
19
19
|
*
|
|
20
|
-
* Search indexing is the canonical reason this matters: it imports
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
20
|
+
* Search indexing is the canonical reason this matters: it imports the
|
|
21
|
+
* server-only app client boundary from `byline/clients.server.ts`. Importing
|
|
22
|
+
* that from the schema would drag server code into the browser bundle (fatal
|
|
23
|
+
* in dev). The server bootstrap avoids it by registering these hooks from a
|
|
24
|
+
* server-only module:
|
|
25
25
|
*
|
|
26
|
-
* //
|
|
27
|
-
*
|
|
26
|
+
* // collections/server-hooks.ts
|
|
27
|
+
* collections: { docs: () => import('./docs/hooks.js') }
|
|
28
28
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* directly.** See docs/04-collections → "Hooks must not statically import
|
|
35
|
-
* server-only code".
|
|
29
|
+
* No client entry reaches that registry, so this file and its transitive graph
|
|
30
|
+
* are structurally absent from the client bundle without a host-framework
|
|
31
|
+
* transform. `@byline/core` attaches and resolves the loader on the server.
|
|
32
|
+
* The upshot: **inside this file we may statically import server-only modules
|
|
33
|
+
* directly.** See docs/04-collections → "Server-only hook registry".
|
|
36
34
|
*
|
|
37
35
|
* ──────────────────────────────────────────────────────────────────────────
|
|
38
36
|
* Search indexing
|
|
@@ -52,7 +50,7 @@
|
|
|
52
50
|
*/
|
|
53
51
|
|
|
54
52
|
import { defineHooks } from '@byline/core'
|
|
55
|
-
import { getSystemBylineClient } from '
|
|
53
|
+
import { getSystemBylineClient } from '../../clients.server.js'
|
|
56
54
|
|
|
57
55
|
export default defineHooks({
|
|
58
56
|
beforeCreate: async ({ data, collectionPath }) => {
|
|
@@ -62,7 +60,7 @@ export default defineHooks({
|
|
|
62
60
|
console.log(
|
|
63
61
|
`afterCreate: document ${documentId} (version ${documentVersionId}) created in '${collectionPath}'`
|
|
64
62
|
)
|
|
65
|
-
await getSystemBylineClient().collection(
|
|
63
|
+
await getSystemBylineClient().collection('docs').indexDocument(documentId)
|
|
66
64
|
},
|
|
67
65
|
beforeUpdate: async ({ data, collectionPath }) => {
|
|
68
66
|
console.log(`beforeUpdate: updating a document in '${collectionPath}'`, data)
|
|
@@ -71,7 +69,19 @@ export default defineHooks({
|
|
|
71
69
|
console.log(
|
|
72
70
|
`afterUpdate: document ${documentId} (version ${documentVersionId}) in '${collectionPath}' updated`
|
|
73
71
|
)
|
|
74
|
-
await getSystemBylineClient().collection(
|
|
72
|
+
await getSystemBylineClient().collection('docs').indexDocument(documentId)
|
|
73
|
+
},
|
|
74
|
+
// Path and advertised-locale edits are immediate document-grain writes, not
|
|
75
|
+
// content updates. Re-index their published view through this dedicated event.
|
|
76
|
+
afterSystemFieldsChange: async ({ documentId, collectionPath, requested, changed }) => {
|
|
77
|
+
console.log(
|
|
78
|
+
`afterSystemFieldsChange: ${documentId} in '${collectionPath}' changed`,
|
|
79
|
+
changed
|
|
80
|
+
)
|
|
81
|
+
// Advertised locales affect alternates/sitemaps, not indexed content.
|
|
82
|
+
if (requested.path) {
|
|
83
|
+
await getSystemBylineClient().collection('docs').indexDocument(documentId)
|
|
84
|
+
}
|
|
75
85
|
},
|
|
76
86
|
beforeStatusChange: async ({ documentId, collectionPath, previousStatus, nextStatus }) => {
|
|
77
87
|
console.log(
|
|
@@ -85,21 +95,21 @@ export default defineHooks({
|
|
|
85
95
|
console.log(
|
|
86
96
|
`afterStatusChange: ${documentId} in '${collectionPath}' ${previousStatus} → ${nextStatus}`
|
|
87
97
|
)
|
|
88
|
-
await getSystemBylineClient().collection(
|
|
98
|
+
await getSystemBylineClient().collection('docs').indexDocument(documentId)
|
|
89
99
|
},
|
|
90
100
|
beforeUnpublish: async ({ documentId, collectionPath }) => {
|
|
91
101
|
console.log(`beforeUnpublish: unpublishing ${documentId} in '${collectionPath}'`)
|
|
92
102
|
},
|
|
93
103
|
afterUnpublish: async ({ documentId, collectionPath }) => {
|
|
94
104
|
console.log(`afterUnpublish: ${documentId} in '${collectionPath}' unpublished`)
|
|
95
|
-
await getSystemBylineClient().collection(
|
|
105
|
+
await getSystemBylineClient().collection('docs').indexDocument(documentId)
|
|
96
106
|
},
|
|
97
107
|
beforeDelete: async ({ documentId, collectionPath }) => {
|
|
98
108
|
console.log(`beforeDelete: deleting ${documentId} in '${collectionPath}'`)
|
|
99
109
|
},
|
|
100
110
|
afterDelete: async ({ documentId, collectionPath }) => {
|
|
101
111
|
console.log(`afterDelete: ${documentId} in '${collectionPath}' deleted`)
|
|
102
|
-
await getSystemBylineClient().collection(
|
|
112
|
+
await getSystemBylineClient().collection('docs').removeFromIndex(documentId)
|
|
103
113
|
},
|
|
104
114
|
// Fires on any structural tree change (place / reorder / re-parent /
|
|
105
115
|
// promote-on-delete) for a `tree: true` collection. The payload carries the
|
|
@@ -41,13 +41,22 @@ export const Docs = defineCollection({
|
|
|
41
41
|
// placement from the source directory layout (see
|
|
42
42
|
// byline/scripts/import-docs.ts --tree).
|
|
43
43
|
tree: true,
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
// the
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
//
|
|
44
|
+
// Admin list-view quick-search. The collection list route's search box
|
|
45
|
+
// matches these top-level text-store fields with substring queries (ILIKE, in
|
|
46
|
+
// the Postgres adapter) against `store_text` — a lightweight "find the row I
|
|
47
|
+
// mean" affordance that needs no indexing and no lifecycle hooks. Falls back
|
|
48
|
+
// to `useAsTitle` when omitted; declared explicitly here for guidance.
|
|
49
|
+
listSearch: ['title'],
|
|
50
|
+
// Search-*provider* config (docs/05-reading-and-delivery/07-search.md) —
|
|
51
|
+
// distinct from `listSearch` above. Each key names the part a field plays in
|
|
52
|
+
// the index: `body` fields feed the full-text search vector (`title` is
|
|
53
|
+
// display-only unless listed here, so we include it and boost it into the
|
|
54
|
+
// heaviest weight class); `facets`, `filters`, and `zones` round out the
|
|
55
|
+
// surface. This drives the pluggable SearchProvider (full-text search in
|
|
56
|
+
// @byline/search-postgres) and requires a `search` provider registered in
|
|
57
|
+
// byline/server.config.ts. Unlike `listSearch`, `search` is inert on its own
|
|
58
|
+
// — opting in here is what makes the index / reindex / deindex lifecycle
|
|
59
|
+
// hooks in ./hooks.ts keep this collection's provider index in sync.
|
|
51
60
|
// `content` is a container (blocks) field: naming it walks every text-bearing
|
|
52
61
|
// child (richtext body, image alt + caption) into the searchable body —
|
|
53
62
|
// omit it and document bodies are silently absent from the index.
|
|
@@ -56,17 +65,8 @@ export const Docs = defineCollection({
|
|
|
56
65
|
useAsPath: 'title',
|
|
57
66
|
advertiseLocales: true, // Renders the available-locales sidebar widget.
|
|
58
67
|
linksInEditor: true, // See type definition for details.
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
// admin too (the admin reads field config from it), so anything the schema
|
|
62
|
-
// *statically imports* ships to the client. Search indexing needs a
|
|
63
|
-
// server-only import (`getSystemBylineClient` from
|
|
64
|
-
// `@byline/host-tanstack-start`), which would crash the browser bundle if
|
|
65
|
-
// imported here. The **loader form** — a thunk that dynamically imports a
|
|
66
|
-
// sibling module — keeps `./hooks.ts` and its entire import graph out of
|
|
67
|
-
// the client bundle while running exactly like inline hooks on the server.
|
|
68
|
-
// See ./hooks.ts for the worked example and the full explanation.
|
|
69
|
-
hooks: () => import('./hooks.js'),
|
|
68
|
+
// Server-only lifecycle hooks are registered in ../server-hooks.ts so this
|
|
69
|
+
// schema remains portable across host frameworks and safe in client graphs.
|
|
70
70
|
fields: [
|
|
71
71
|
{
|
|
72
72
|
name: 'title',
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { InferCollectionRegistry } from '@byline/core'
|
|
10
|
+
|
|
11
|
+
import type { CollectionFieldsByPath } from '../generated/collection-types.js'
|
|
12
|
+
|
|
13
|
+
import { Docs } from './docs/schema.js'
|
|
14
|
+
import { Media } from './media/schema.js'
|
|
15
|
+
import { News } from './news/schema.js'
|
|
16
|
+
import { NewsCategories } from './news-categories/schema.js'
|
|
17
|
+
import { Pages } from './pages/schema.js'
|
|
18
|
+
|
|
19
|
+
export const collections = [Docs, News, Pages, Media, NewsCategories] as const
|
|
20
|
+
|
|
21
|
+
export type InferredBylineCollections = InferCollectionRegistry<typeof collections>
|
|
22
|
+
export type BylineCollections = CollectionFieldsByPath
|
|
@@ -19,11 +19,16 @@
|
|
|
19
19
|
* - Top + bottom pagination (RouterPager)
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import {
|
|
22
|
+
import { useRouterState } from '@tanstack/react-router'
|
|
23
23
|
|
|
24
24
|
import type { ListViewComponentProps, StoredFileValue, WorkflowStatus } from '@byline/core'
|
|
25
25
|
import type { AnyCollectionSchemaTypes } from '@byline/core/zod-schemas'
|
|
26
26
|
import { RouterPager } from '@byline/host-tanstack-start/admin-shell/chrome/router-pager'
|
|
27
|
+
import {
|
|
28
|
+
Link,
|
|
29
|
+
useNavigate,
|
|
30
|
+
} from '@byline/host-tanstack-start/admin-shell/chrome/loose-router'
|
|
31
|
+
import { getAdminRoutePath } from '@byline/host-tanstack-start/routes/admin-path'
|
|
27
32
|
import { Container, IconButton, LoaderRing, PlusIcon, Search, Section, Select } from '@byline/ui/react'
|
|
28
33
|
import { LocalDateTime } from '@byline/admin/react'
|
|
29
34
|
|
|
@@ -128,7 +133,7 @@ export function MediaListView({
|
|
|
128
133
|
delete params.page
|
|
129
134
|
params.query = query
|
|
130
135
|
navigate({
|
|
131
|
-
to: '
|
|
136
|
+
to: getAdminRoutePath('collections', '$collection'),
|
|
132
137
|
params: { collection: collectionPath },
|
|
133
138
|
search: params,
|
|
134
139
|
})
|
|
@@ -140,7 +145,7 @@ export function MediaListView({
|
|
|
140
145
|
delete params.page
|
|
141
146
|
delete params.query
|
|
142
147
|
navigate({
|
|
143
|
-
to: '
|
|
148
|
+
to: getAdminRoutePath('collections', '$collection'),
|
|
144
149
|
params: { collection: collectionPath },
|
|
145
150
|
search: params,
|
|
146
151
|
})
|
|
@@ -158,7 +163,7 @@ export function MediaListView({
|
|
|
158
163
|
params.order = order
|
|
159
164
|
params.desc = desc
|
|
160
165
|
navigate({
|
|
161
|
-
to: '
|
|
166
|
+
to: getAdminRoutePath('collections', '$collection'),
|
|
162
167
|
params: { collection: collectionPath },
|
|
163
168
|
search: params,
|
|
164
169
|
})
|
|
@@ -178,7 +183,7 @@ export function MediaListView({
|
|
|
178
183
|
render={
|
|
179
184
|
<Link
|
|
180
185
|
className={styles['create-link']}
|
|
181
|
-
to=
|
|
186
|
+
to={getAdminRoutePath('collections', '$collection', 'create')}
|
|
182
187
|
params={{ collection: collectionPath }}
|
|
183
188
|
/>
|
|
184
189
|
}
|
|
@@ -239,7 +244,7 @@ export function MediaListView({
|
|
|
239
244
|
return (
|
|
240
245
|
<Link
|
|
241
246
|
key={doc.id}
|
|
242
|
-
to=
|
|
247
|
+
to={getAdminRoutePath('collections', '$collection', '$id')}
|
|
243
248
|
params={{ collection: collectionPath, id: doc.id }}
|
|
244
249
|
className={styles.card}
|
|
245
250
|
>
|
|
@@ -50,6 +50,19 @@ export const Media = defineCollection({
|
|
|
50
50
|
archived: { label: 'Archived', verb: 'Archive' },
|
|
51
51
|
}),
|
|
52
52
|
showStats: true,
|
|
53
|
+
// Admin list-view quick-search. The collection list route's search box
|
|
54
|
+
// matches these top-level text-store fields with substring queries (ILIKE, in
|
|
55
|
+
// the Postgres adapter) against `store_text` — a lightweight "find the row I
|
|
56
|
+
// mean" affordance that needs no indexing and no lifecycle hooks. Falls back
|
|
57
|
+
// to `useAsTitle` when omitted; declared explicitly here for guidance.
|
|
58
|
+
listSearch: ['title'],
|
|
59
|
+
// Search-*provider* config (docs/05-reading-and-delivery/07-search.md) —
|
|
60
|
+
// distinct from `listSearch` above. This drives the pluggable SearchProvider
|
|
61
|
+
// (full-text search in @byline/search-postgres) and offers richer options:
|
|
62
|
+
// per-field weighting/`boost`, `facets`, `filters`, and `zones`. Unlike
|
|
63
|
+
// `listSearch`, `search` is inert on its own — it MUST be paired with index /
|
|
64
|
+
// reindex / deindex document-lifecycle hooks that keep the provider index in
|
|
65
|
+
// sync on create / update / publish / delete (see this collection's hooks).
|
|
53
66
|
search: { body: ['title', 'caption'] },
|
|
54
67
|
fields: [
|
|
55
68
|
{
|
|
@@ -115,6 +128,9 @@ export const Media = defineCollection({
|
|
|
115
128
|
quality: 55,
|
|
116
129
|
},
|
|
117
130
|
],
|
|
131
|
+
// Worked example: definition-attached inline hooks are appropriate
|
|
132
|
+
// when their entire implementation is isomorphic-safe. Hooks that
|
|
133
|
+
// import server-only code belong in collections/server-hooks.ts.
|
|
118
134
|
hooks: {
|
|
119
135
|
beforeStore: (ctx: BeforeStoreContext) => {
|
|
120
136
|
console.log('beforeStore hook called', ctx)
|
|
@@ -30,6 +30,19 @@ export const News = defineCollection({
|
|
|
30
30
|
archived: { label: 'Archived', verb: 'Archive' },
|
|
31
31
|
}),
|
|
32
32
|
showStats: true,
|
|
33
|
+
// Admin list-view quick-search. The collection list route's search box
|
|
34
|
+
// matches these top-level text-store fields with substring queries (ILIKE, in
|
|
35
|
+
// the Postgres adapter) against `store_text` — a lightweight "find the row I
|
|
36
|
+
// mean" affordance that needs no indexing and no lifecycle hooks. Falls back
|
|
37
|
+
// to `useAsTitle` when omitted; declared explicitly here for guidance.
|
|
38
|
+
listSearch: ['title'],
|
|
39
|
+
// Search-*provider* config (docs/05-reading-and-delivery/07-search.md) —
|
|
40
|
+
// distinct from `listSearch` above. This drives the pluggable SearchProvider
|
|
41
|
+
// (full-text search in @byline/search-postgres) and offers richer options:
|
|
42
|
+
// per-field weighting/`boost`, `facets`, `filters`, and `zones`. Unlike
|
|
43
|
+
// `listSearch`, `search` is inert on its own — it MUST be paired with index /
|
|
44
|
+
// reindex / deindex document-lifecycle hooks that keep the provider index in
|
|
45
|
+
// sync on create / update / publish / delete (see this collection's hooks).
|
|
33
46
|
search: { body: ['title'] },
|
|
34
47
|
useAsTitle: 'title',
|
|
35
48
|
useAsPath: 'title',
|
|
@@ -86,8 +99,9 @@ export const News = defineCollection({
|
|
|
86
99
|
})
|
|
87
100
|
|
|
88
101
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
102
|
+
* Schema-local field shape for hooks and other collection implementation details.
|
|
103
|
+
* Frontend/client code should import `NewsFields` from
|
|
104
|
+
* `byline/generated/collection-types.ts`; ordinary reads infer it from the
|
|
105
|
+
* client registry, while populate/select projections may supply an overlay.
|
|
92
106
|
*/
|
|
93
107
|
export type NewsFields = CollectionFieldData<typeof News>
|
|
@@ -21,6 +21,19 @@ export const NewsCategories = defineCollection({
|
|
|
21
21
|
// `published` and the form shows only Save / Close.
|
|
22
22
|
workflow: SINGLE_STATUS_WORKFLOW,
|
|
23
23
|
showStats: true,
|
|
24
|
+
// Admin list-view quick-search. The collection list route's search box
|
|
25
|
+
// matches these top-level text-store fields with substring queries (ILIKE, in
|
|
26
|
+
// the Postgres adapter) against `store_text` — a lightweight "find the row I
|
|
27
|
+
// mean" affordance that needs no indexing and no lifecycle hooks. Falls back
|
|
28
|
+
// to `useAsTitle` when omitted; declared explicitly here for guidance.
|
|
29
|
+
listSearch: ['name'],
|
|
30
|
+
// Search-*provider* config (docs/05-reading-and-delivery/07-search.md) —
|
|
31
|
+
// distinct from `listSearch` above. This drives the pluggable SearchProvider
|
|
32
|
+
// (full-text search in @byline/search-postgres) and offers richer options:
|
|
33
|
+
// per-field weighting/`boost`, `facets`, `filters`, and `zones`. Unlike
|
|
34
|
+
// `listSearch`, `search` is inert on its own — it MUST be paired with index /
|
|
35
|
+
// reindex / deindex document-lifecycle hooks that keep the provider index in
|
|
36
|
+
// sync on create / update / publish / delete (see this collection's hooks).
|
|
24
37
|
search: { body: ['name'] },
|
|
25
38
|
useAsTitle: 'name',
|
|
26
39
|
useAsPath: 'name',
|
|
@@ -32,6 +32,19 @@ export const Pages = defineCollection({
|
|
|
32
32
|
archived: { label: 'Archived', verb: 'Archive' },
|
|
33
33
|
}),
|
|
34
34
|
showStats: true,
|
|
35
|
+
// Admin list-view quick-search. The collection list route's search box
|
|
36
|
+
// matches these top-level text-store fields with substring queries (ILIKE, in
|
|
37
|
+
// the Postgres adapter) against `store_text` — a lightweight "find the row I
|
|
38
|
+
// mean" affordance that needs no indexing and no lifecycle hooks. Falls back
|
|
39
|
+
// to `useAsTitle` when omitted; declared explicitly here for guidance.
|
|
40
|
+
listSearch: ['title'],
|
|
41
|
+
// Search-*provider* config (docs/05-reading-and-delivery/07-search.md) —
|
|
42
|
+
// distinct from `listSearch` above. This drives the pluggable SearchProvider
|
|
43
|
+
// (full-text search in @byline/search-postgres) and offers richer options:
|
|
44
|
+
// per-field weighting/`boost`, `facets`, `filters`, and `zones`. Unlike
|
|
45
|
+
// `listSearch`, `search` is inert on its own — it MUST be paired with index /
|
|
46
|
+
// reindex / deindex document-lifecycle hooks that keep the provider index in
|
|
47
|
+
// sync on create / update / publish / delete (see this collection's hooks).
|
|
35
48
|
search: { body: ['title'] },
|
|
36
49
|
useAsTitle: 'title',
|
|
37
50
|
useAsPath: 'title',
|
|
@@ -28,7 +28,7 @@ import { AiFieldPanel } from './ai-widgets/ai-field-panel.js'
|
|
|
28
28
|
*
|
|
29
29
|
* @example
|
|
30
30
|
* ```ts
|
|
31
|
-
* //
|
|
31
|
+
* // byline/collections/news/admin.tsx
|
|
32
32
|
* fields: {
|
|
33
33
|
* summary: aiTextAreaFieldAdmin({
|
|
34
34
|
* components: { HelpText: SummaryLength },
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* - `LexicalRichTextAi` — a `RichTextEditorComponent` that registers
|
|
14
14
|
* the AI assistant by adding `AiLexicalExtension` to the editor's
|
|
15
15
|
* extension graph. Register globally in
|
|
16
|
-
* `
|
|
16
|
+
* `byline/admin.config.ts` under
|
|
17
17
|
* `fields.richText.editor` to AI-enable every richtext field.
|
|
18
18
|
* - `aiRichTextAdmin()` — a `FieldAdminConfig` factory. Drop into a
|
|
19
19
|
* collection's `fields` map in `<collection>/admin.tsx` to opt one
|
|
@@ -38,7 +38,7 @@ import { builtInExtensions, lexicalEditor } from '@byline/richtext-lexical/confi
|
|
|
38
38
|
* drawer via `ReactExtension.decorators` — no `featureAfterEditor`, no
|
|
39
39
|
* React-context registry hop.
|
|
40
40
|
*
|
|
41
|
-
* **Global** opt-in — register in `
|
|
41
|
+
* **Global** opt-in — register in `byline/admin.config.ts`:
|
|
42
42
|
*
|
|
43
43
|
* ```ts
|
|
44
44
|
* fields: {
|
|
@@ -77,7 +77,7 @@ export const LexicalRichTextAi = lexicalEditor((c) => {
|
|
|
77
77
|
*
|
|
78
78
|
* @example
|
|
79
79
|
* ```ts
|
|
80
|
-
* //
|
|
80
|
+
* // byline/collections/news/admin.tsx
|
|
81
81
|
* import { aiRichTextAdmin } from '../../fields/lexical-richtext-ai.js'
|
|
82
82
|
*
|
|
83
83
|
* fields: {
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// Generated by @byline/core/codegen
|
|
2
|
+
// Format version: 1
|
|
3
|
+
// Hash: a32bba417302b4e2c90703e47a1f326d4db64cabd11194f8eed816e78442178e
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
JsonValue,
|
|
7
|
+
RelatedDocumentValue,
|
|
8
|
+
StoredFileValue,
|
|
9
|
+
} from '@byline/core'
|
|
10
|
+
|
|
11
|
+
export type PhotoBlockData = {
|
|
12
|
+
_id: string
|
|
13
|
+
_type: 'photoBlock'
|
|
14
|
+
display?: 'default' | 'wide' | 'full_width' | undefined
|
|
15
|
+
photo?: RelatedDocumentValue | undefined
|
|
16
|
+
alt: string
|
|
17
|
+
caption?: JsonValue | undefined
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type PhotoBlockDataAllLocales = {
|
|
21
|
+
_id: string
|
|
22
|
+
_type: 'photoBlock'
|
|
23
|
+
display?: 'default' | 'wide' | 'full_width' | undefined
|
|
24
|
+
photo?: RelatedDocumentValue | undefined
|
|
25
|
+
alt: string
|
|
26
|
+
caption?: { [locale: string]: JsonValue } | undefined
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type RichTextBlockData = {
|
|
30
|
+
_id: string
|
|
31
|
+
_type: 'richTextBlock'
|
|
32
|
+
richText: JsonValue
|
|
33
|
+
constrainedWidth?: boolean | undefined
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type RichTextBlockDataAllLocales = {
|
|
37
|
+
_id: string
|
|
38
|
+
_type: 'richTextBlock'
|
|
39
|
+
richText: { [locale: string]: JsonValue }
|
|
40
|
+
constrainedWidth?: boolean | undefined
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type DocsFields = {
|
|
44
|
+
title: string
|
|
45
|
+
summary: string
|
|
46
|
+
featureImage?: RelatedDocumentValue | undefined
|
|
47
|
+
publishedOn: Date
|
|
48
|
+
content?: Array<RichTextBlockData | PhotoBlockData> | undefined
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type DocsFieldsAllLocales = {
|
|
52
|
+
title: { [locale: string]: string }
|
|
53
|
+
summary: { [locale: string]: string }
|
|
54
|
+
featureImage?: RelatedDocumentValue | undefined
|
|
55
|
+
publishedOn: Date
|
|
56
|
+
content?: Array<RichTextBlockDataAllLocales | PhotoBlockDataAllLocales> | undefined
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type MediaFields = {
|
|
60
|
+
image: StoredFileValue
|
|
61
|
+
title: string
|
|
62
|
+
altText: string
|
|
63
|
+
caption?: string | undefined
|
|
64
|
+
credit?: string | undefined
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export type MediaFieldsAllLocales = {
|
|
68
|
+
image: StoredFileValue
|
|
69
|
+
title: string
|
|
70
|
+
altText: string
|
|
71
|
+
caption?: string | undefined
|
|
72
|
+
credit?: string | undefined
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type NewsCategoriesFields = {
|
|
76
|
+
name: string
|
|
77
|
+
description: string
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type NewsCategoriesFieldsAllLocales = {
|
|
81
|
+
name: { [locale: string]: string }
|
|
82
|
+
description: { [locale: string]: string }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export type NewsFields = {
|
|
86
|
+
title: string
|
|
87
|
+
summary: string
|
|
88
|
+
category: RelatedDocumentValue
|
|
89
|
+
featureImage?: RelatedDocumentValue | undefined
|
|
90
|
+
featured?: boolean | undefined
|
|
91
|
+
content: JsonValue
|
|
92
|
+
publishedOn: Date
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export type NewsFieldsAllLocales = {
|
|
96
|
+
title: { [locale: string]: string }
|
|
97
|
+
summary: { [locale: string]: string }
|
|
98
|
+
category: RelatedDocumentValue
|
|
99
|
+
featureImage?: RelatedDocumentValue | undefined
|
|
100
|
+
featured?: boolean | undefined
|
|
101
|
+
content: { [locale: string]: JsonValue }
|
|
102
|
+
publishedOn: Date
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type PagesFields = {
|
|
106
|
+
title: string
|
|
107
|
+
summary: string
|
|
108
|
+
featureImage?: RelatedDocumentValue | undefined
|
|
109
|
+
area: 'root' | 'about' | 'legal'
|
|
110
|
+
content?: Array<RichTextBlockData | PhotoBlockData> | undefined
|
|
111
|
+
publishedOn: Date
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export type PagesFieldsAllLocales = {
|
|
115
|
+
title: { [locale: string]: string }
|
|
116
|
+
summary: { [locale: string]: string }
|
|
117
|
+
featureImage?: RelatedDocumentValue | undefined
|
|
118
|
+
area: 'root' | 'about' | 'legal'
|
|
119
|
+
content?: Array<RichTextBlockDataAllLocales | PhotoBlockDataAllLocales> | undefined
|
|
120
|
+
publishedOn: Date
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export type CollectionFieldsByPath = {
|
|
124
|
+
docs: DocsFields
|
|
125
|
+
media: MediaFields
|
|
126
|
+
news: NewsFields
|
|
127
|
+
'news-categories': NewsCategoriesFields
|
|
128
|
+
pages: PagesFields
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export type CollectionFieldsAllLocalesByPath = {
|
|
132
|
+
docs: DocsFieldsAllLocales
|
|
133
|
+
media: MediaFieldsAllLocales
|
|
134
|
+
news: NewsFieldsAllLocales
|
|
135
|
+
'news-categories': NewsCategoriesFieldsAllLocales
|
|
136
|
+
pages: PagesFieldsAllLocales
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export type CollectionPath = keyof CollectionFieldsByPath
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
* translation bundle.
|
|
13
13
|
*
|
|
14
14
|
* The locale arrays themselves live in `./locales.ts`, a dependency-free leaf
|
|
15
|
-
* module
|
|
16
|
-
* translation graph this file depends on. Re-exported here for
|
|
17
|
-
* with existing `~/i18n` importers.
|
|
15
|
+
* module exposed to public frontend code by `./public.ts`, without pulling in
|
|
16
|
+
* the admin translation graph this file depends on. Re-exported here for
|
|
17
|
+
* back-compat with existing `~/i18n` importers.
|
|
18
18
|
*
|
|
19
19
|
* `interface` locales govern the CMS admin UI language.
|
|
20
20
|
* `content` locales govern the languages a document can be published in.
|