@byline/cli 4.9.0 → 4.10.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 +56 -15
- package/dist/cli.js +16 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/doctor.d.ts +8 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +8 -5
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.d.ts +2 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/setup-checks.d.ts +2 -0
- package/dist/commands/setup-checks.d.ts.map +1 -1
- package/dist/commands/setup-checks.js +14 -6
- package/dist/commands/setup-checks.js.map +1 -1
- package/dist/commands/setup-flow.test.d.ts +2 -0
- package/dist/commands/setup-flow.test.d.ts.map +1 -0
- package/dist/commands/setup-flow.test.js +230 -0
- package/dist/commands/setup-flow.test.js.map +1 -0
- package/dist/commands/setup.d.ts +30 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +71 -35
- package/dist/commands/setup.js.map +1 -1
- package/dist/context.d.ts +4 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +3 -0
- package/dist/context.js.map +1 -1
- package/dist/lib/baseline-drift.test.d.ts +2 -0
- package/dist/lib/baseline-drift.test.d.ts.map +1 -0
- package/dist/lib/baseline-drift.test.js +97 -0
- package/dist/lib/baseline-drift.test.js.map +1 -0
- package/dist/lib/baseline.d.ts +10 -0
- package/dist/lib/baseline.d.ts.map +1 -0
- package/dist/lib/baseline.js +12 -0
- package/dist/lib/baseline.js.map +1 -0
- package/dist/lib/database/adapters.d.ts +54 -0
- package/dist/lib/database/adapters.d.ts.map +1 -0
- package/dist/lib/database/adapters.js +83 -0
- package/dist/lib/database/adapters.js.map +1 -0
- package/dist/lib/database/adapters.test.d.ts +2 -0
- package/dist/lib/database/adapters.test.d.ts.map +1 -0
- package/dist/lib/database/adapters.test.js +36 -0
- package/dist/lib/database/adapters.test.js.map +1 -0
- package/dist/lib/database/dialect.d.ts +12 -0
- package/dist/lib/database/dialect.d.ts.map +1 -0
- package/dist/lib/database/dialect.js +53 -0
- package/dist/lib/database/dialect.js.map +1 -0
- package/dist/lib/database/dialect.test.d.ts +2 -0
- package/dist/lib/database/dialect.test.d.ts.map +1 -0
- package/dist/lib/database/dialect.test.js +58 -0
- package/dist/lib/database/dialect.test.js.map +1 -0
- package/dist/lib/database/mysql.d.ts +21 -0
- package/dist/lib/database/mysql.d.ts.map +1 -0
- package/dist/lib/database/mysql.js +152 -0
- package/dist/lib/database/mysql.js.map +1 -0
- package/dist/lib/database/mysql.test.d.ts +2 -0
- package/dist/lib/database/mysql.test.d.ts.map +1 -0
- package/dist/lib/database/mysql.test.js +45 -0
- package/dist/lib/database/mysql.test.js.map +1 -0
- package/dist/lib/database/postgres.d.ts +8 -0
- package/dist/lib/database/postgres.d.ts.map +1 -0
- package/dist/lib/database/postgres.js +142 -0
- package/dist/lib/database/postgres.js.map +1 -0
- package/dist/lib/database/postgres.test.d.ts +2 -0
- package/dist/lib/database/postgres.test.d.ts.map +1 -0
- package/dist/lib/database/postgres.test.js +14 -0
- package/dist/lib/database/postgres.test.js.map +1 -0
- package/dist/lib/database/provisioner.d.ts +35 -0
- package/dist/lib/database/provisioner.d.ts.map +1 -0
- package/dist/lib/database/provisioner.js +20 -0
- package/dist/lib/database/provisioner.js.map +1 -0
- package/dist/lib/database/provisioner.test.d.ts +2 -0
- package/dist/lib/database/provisioner.test.d.ts.map +1 -0
- package/dist/lib/database/provisioner.test.js +32 -0
- package/dist/lib/database/provisioner.test.js.map +1 -0
- package/dist/lib/database/selection.d.ts +11 -0
- package/dist/lib/database/selection.d.ts.map +1 -0
- package/dist/lib/database/selection.js +52 -0
- package/dist/lib/database/selection.js.map +1 -0
- package/dist/lib/database/selection.test.d.ts +2 -0
- package/dist/lib/database/selection.test.d.ts.map +1 -0
- package/dist/lib/database/selection.test.js +58 -0
- package/dist/lib/database/selection.test.js.map +1 -0
- package/dist/lib/database/state.d.ts +4 -0
- package/dist/lib/database/state.d.ts.map +1 -0
- package/dist/lib/database/state.js +10 -0
- package/dist/lib/database/state.js.map +1 -0
- package/dist/lib/database/state.test.d.ts +2 -0
- package/dist/lib/database/state.test.d.ts.map +1 -0
- package/dist/lib/database/state.test.js +17 -0
- package/dist/lib/database/state.test.js.map +1 -0
- package/dist/lib/database/urls.d.ts +19 -0
- package/dist/lib/database/urls.d.ts.map +1 -0
- package/dist/lib/database/urls.js +14 -0
- package/dist/lib/database/urls.js.map +1 -0
- package/dist/lib/database/urls.test.d.ts +2 -0
- package/dist/lib/database/urls.test.d.ts.map +1 -0
- package/dist/lib/database/urls.test.js +31 -0
- package/dist/lib/database/urls.test.js.map +1 -0
- package/dist/lib/dependency-version.d.ts.map +1 -1
- package/dist/lib/dependency-version.js +5 -4
- package/dist/lib/dependency-version.js.map +1 -1
- package/dist/lib/template-hook-boundary.test.node.js +9 -4
- package/dist/lib/template-hook-boundary.test.node.js.map +1 -1
- package/dist/manifest/deps.d.ts +8 -1
- package/dist/manifest/deps.d.ts.map +1 -1
- package/dist/manifest/deps.js +40 -13
- package/dist/manifest/deps.js.map +1 -1
- package/dist/manifest/env.d.ts +4 -1
- package/dist/manifest/env.d.ts.map +1 -1
- package/dist/manifest/env.js +12 -6
- package/dist/manifest/env.js.map +1 -1
- package/dist/phases/db-init.d.ts +2 -1
- package/dist/phases/db-init.d.ts.map +1 -1
- package/dist/phases/db-init.js +141 -124
- package/dist/phases/db-init.js.map +1 -1
- package/dist/phases/db-init.test.d.ts +2 -0
- package/dist/phases/db-init.test.d.ts.map +1 -0
- package/dist/phases/db-init.test.js +144 -0
- package/dist/phases/db-init.test.js.map +1 -0
- package/dist/phases/db.d.ts +0 -8
- package/dist/phases/db.d.ts.map +1 -1
- package/dist/phases/db.js +69 -49
- package/dist/phases/db.js.map +1 -1
- package/dist/phases/db.test.d.ts +2 -0
- package/dist/phases/db.test.d.ts.map +1 -0
- package/dist/phases/db.test.js +69 -0
- package/dist/phases/db.test.js.map +1 -0
- package/dist/phases/deps.d.ts.map +1 -1
- package/dist/phases/deps.js +7 -4
- package/dist/phases/deps.js.map +1 -1
- package/dist/phases/deps.test.js +67 -5
- package/dist/phases/deps.test.js.map +1 -1
- package/dist/phases/env.d.ts.map +1 -1
- package/dist/phases/env.js +30 -21
- package/dist/phases/env.js.map +1 -1
- package/dist/phases/env.test.d.ts +2 -0
- package/dist/phases/env.test.d.ts.map +1 -0
- package/dist/phases/env.test.js +79 -0
- package/dist/phases/env.test.js.map +1 -0
- package/dist/phases/scaffold-smoke.test.js +44 -10
- package/dist/phases/scaffold-smoke.test.js.map +1 -1
- package/dist/phases/scaffold.d.ts.map +1 -1
- package/dist/phases/scaffold.js +32 -23
- package/dist/phases/scaffold.js.map +1 -1
- package/dist/phases/scaffold.test.js +22 -0
- package/dist/phases/scaffold.test.js.map +1 -1
- package/dist/phases/wire/vite-config.d.ts.map +1 -1
- package/dist/phases/wire/vite-config.js +2 -0
- package/dist/phases/wire/vite-config.js.map +1 -1
- package/dist/phases/wire/vite-config.test.js +25 -2
- package/dist/phases/wire/vite-config.test.js.map +1 -1
- package/dist/state.d.ts +6 -0
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +40 -4
- package/dist/state.js.map +1 -1
- package/dist/state.test.d.ts +2 -0
- package/dist/state.test.d.ts.map +1 -0
- package/dist/state.test.js +77 -0
- package/dist/state.test.js.map +1 -0
- package/dist/templates/byline-examples/collections/docs/schema.ts +7 -7
- package/dist/templates/byline-examples/collections/media/schema.ts +6 -6
- package/dist/templates/byline-examples/collections/news/schema.ts +6 -6
- package/dist/templates/byline-examples/collections/news-categories/schema.ts +6 -6
- package/dist/templates/byline-examples/collections/pages/schema.ts +6 -6
- package/dist/templates/dialects/mysql/byline/server.config.ts +105 -0
- package/dist/templates/dialects/mysql/byline-examples/server.config.ts +115 -0
- package/dist/templates/host/vite.config.ts +4 -3
- package/dist/templates/migrations/mysql/0000_unknown_kulan_gath.sql +412 -0
- package/dist/templates/migrations/mysql/meta/_journal.json +13 -0
- package/dist/test-helpers.d.ts +14 -2
- package/dist/test-helpers.d.ts.map +1 -1
- package/dist/test-helpers.js +12 -9
- package/dist/test-helpers.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -2
- package/dist/templates/migrations/meta/0000_snapshot.json +0 -3471
- /package/dist/templates/{byline → dialects/postgres/byline}/server.config.ts +0 -0
- /package/dist/templates/{byline-examples → dialects/postgres/byline-examples}/scripts/backfill-version-locales.ts +0 -0
- /package/dist/templates/{byline-examples → dialects/postgres/byline-examples}/scripts/re-anchor.ts +0 -0
- /package/dist/templates/{byline-examples → dialects/postgres/byline-examples}/server.config.ts +0 -0
- /package/dist/templates/migrations/{0000_mushy_luckman.sql → postgres/0000_mushy_luckman.sql} +0 -0
- /package/dist/templates/migrations/{meta → postgres/meta}/_journal.json +0 -0
|
@@ -42,19 +42,19 @@ export const Docs = defineCollection({
|
|
|
42
42
|
// byline/scripts/import-docs.ts --tree).
|
|
43
43
|
tree: true,
|
|
44
44
|
// Admin list-view quick-search. The collection list route's search box
|
|
45
|
-
// matches these top-level text-store fields with substring
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
45
|
+
// matches these top-level text-store fields with adapter-native substring
|
|
46
|
+
// queries against `store_text` — a lightweight "find the row I mean"
|
|
47
|
+
// affordance that needs no indexing and no lifecycle hooks. Falls back to
|
|
48
|
+
// `useAsTitle` when omitted; declared explicitly here for guidance.
|
|
49
49
|
listSearch: ['title'],
|
|
50
50
|
// Search-*provider* config (docs/06-search/index.md) —
|
|
51
51
|
// distinct from `listSearch` above. Each key names the part a field plays in
|
|
52
52
|
// the index: `body` fields feed the full-text search vector (`title` is
|
|
53
53
|
// display-only unless listed here, so we include it and boost it into the
|
|
54
54
|
// heaviest weight class); `facets`, `filters`, and `zones` round out the
|
|
55
|
-
// surface. This drives the
|
|
56
|
-
//
|
|
57
|
-
//
|
|
55
|
+
// surface. This drives the configured full-text SearchProvider and requires
|
|
56
|
+
// a `search` provider registered in byline/server.config.ts. Unlike
|
|
57
|
+
// `listSearch`, `search` is inert on its own
|
|
58
58
|
// — opting in here is what makes the index / reindex / deindex lifecycle
|
|
59
59
|
// hooks in ./hooks.ts keep this collection's provider index in sync.
|
|
60
60
|
// `content` is a container (blocks) field: naming it walks every text-bearing
|
|
@@ -51,14 +51,14 @@ export const Media = defineCollection({
|
|
|
51
51
|
}),
|
|
52
52
|
showStats: true,
|
|
53
53
|
// Admin list-view quick-search. The collection list route's search box
|
|
54
|
-
// matches these top-level text-store fields with substring
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
54
|
+
// matches these top-level text-store fields with adapter-native substring
|
|
55
|
+
// queries against `store_text` — a lightweight "find the row I mean"
|
|
56
|
+
// affordance that needs no indexing and no lifecycle hooks. Falls back to
|
|
57
|
+
// `useAsTitle` when omitted; declared explicitly here for guidance.
|
|
58
58
|
listSearch: ['title'],
|
|
59
59
|
// Search-*provider* config (docs/06-search/index.md) —
|
|
60
|
-
// distinct from `listSearch` above. This drives the
|
|
61
|
-
//
|
|
60
|
+
// distinct from `listSearch` above. This drives the configured full-text
|
|
61
|
+
// SearchProvider and offers richer options:
|
|
62
62
|
// per-field weighting/`boost`, `facets`, `filters`, and `zones`. Unlike
|
|
63
63
|
// `listSearch`, `search` is inert on its own — it MUST be paired with index /
|
|
64
64
|
// reindex / deindex document-lifecycle hooks that keep the provider index in
|
|
@@ -31,14 +31,14 @@ export const News = defineCollection({
|
|
|
31
31
|
}),
|
|
32
32
|
showStats: true,
|
|
33
33
|
// Admin list-view quick-search. The collection list route's search box
|
|
34
|
-
// matches these top-level text-store fields with substring
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
34
|
+
// matches these top-level text-store fields with adapter-native substring
|
|
35
|
+
// queries against `store_text` — a lightweight "find the row I mean"
|
|
36
|
+
// affordance that needs no indexing and no lifecycle hooks. Falls back to
|
|
37
|
+
// `useAsTitle` when omitted; declared explicitly here for guidance.
|
|
38
38
|
listSearch: ['title'],
|
|
39
39
|
// Search-*provider* config (docs/06-search/index.md) —
|
|
40
|
-
// distinct from `listSearch` above. This drives the
|
|
41
|
-
//
|
|
40
|
+
// distinct from `listSearch` above. This drives the configured full-text
|
|
41
|
+
// SearchProvider and offers richer options:
|
|
42
42
|
// per-field weighting/`boost`, `facets`, `filters`, and `zones`. Unlike
|
|
43
43
|
// `listSearch`, `search` is inert on its own — it MUST be paired with index /
|
|
44
44
|
// reindex / deindex document-lifecycle hooks that keep the provider index in
|
|
@@ -22,14 +22,14 @@ export const NewsCategories = defineCollection({
|
|
|
22
22
|
workflow: SINGLE_STATUS_WORKFLOW,
|
|
23
23
|
showStats: true,
|
|
24
24
|
// Admin list-view quick-search. The collection list route's search box
|
|
25
|
-
// matches these top-level text-store fields with substring
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
25
|
+
// matches these top-level text-store fields with adapter-native substring
|
|
26
|
+
// queries against `store_text` — a lightweight "find the row I mean"
|
|
27
|
+
// affordance that needs no indexing and no lifecycle hooks. Falls back to
|
|
28
|
+
// `useAsTitle` when omitted; declared explicitly here for guidance.
|
|
29
29
|
listSearch: ['name'],
|
|
30
30
|
// Search-*provider* config (docs/06-search/index.md) —
|
|
31
|
-
// distinct from `listSearch` above. This drives the
|
|
32
|
-
//
|
|
31
|
+
// distinct from `listSearch` above. This drives the configured full-text
|
|
32
|
+
// SearchProvider and offers richer options:
|
|
33
33
|
// per-field weighting/`boost`, `facets`, `filters`, and `zones`. Unlike
|
|
34
34
|
// `listSearch`, `search` is inert on its own — it MUST be paired with index /
|
|
35
35
|
// reindex / deindex document-lifecycle hooks that keep the provider index in
|
|
@@ -33,14 +33,14 @@ export const Pages = defineCollection({
|
|
|
33
33
|
}),
|
|
34
34
|
showStats: true,
|
|
35
35
|
// Admin list-view quick-search. The collection list route's search box
|
|
36
|
-
// matches these top-level text-store fields with substring
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
36
|
+
// matches these top-level text-store fields with adapter-native substring
|
|
37
|
+
// queries against `store_text` — a lightweight "find the row I mean"
|
|
38
|
+
// affordance that needs no indexing and no lifecycle hooks. Falls back to
|
|
39
|
+
// `useAsTitle` when omitted; declared explicitly here for guidance.
|
|
40
40
|
listSearch: ['title'],
|
|
41
41
|
// Search-*provider* config (docs/06-search/index.md) —
|
|
42
|
-
// distinct from `listSearch` above. This drives the
|
|
43
|
-
//
|
|
42
|
+
// distinct from `listSearch` above. This drives the configured full-text
|
|
43
|
+
// SearchProvider and offers richer options:
|
|
44
44
|
// per-field weighting/`boost`, `facets`, `filters`, and `zones`. Unlike
|
|
45
45
|
// `listSearch`, `search` is inert on its own — it MUST be paired with index /
|
|
46
46
|
// reindex / deindex document-lifecycle hooks that keep the provider index in
|
|
@@ -0,0 +1,105 @@
|
|
|
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
|
+
/**
|
|
10
|
+
* Server-side Byline bootstrap. Imported as a side-effect from
|
|
11
|
+
* `src/server.ts` and from scripts that need the configured runtime.
|
|
12
|
+
*
|
|
13
|
+
* This is the minimal scaffold: no example collections or search provider.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { type AdminStore, registerAdminAbilities } from '@byline/admin'
|
|
17
|
+
import { JwtSessionProvider } from '@byline/admin/auth'
|
|
18
|
+
import { getAdminBylineClient } from '@byline/client/server'
|
|
19
|
+
import { type BylineCore, initBylineCore } from '@byline/core'
|
|
20
|
+
import { mysqlAdapter } from '@byline/db-mysql'
|
|
21
|
+
import { createAdminStore } from '@byline/db-mysql/admin'
|
|
22
|
+
import { registerTanstackStartHostBridge } from '@byline/host-tanstack-start/integrations/host-bridge'
|
|
23
|
+
import {
|
|
24
|
+
lexicalEditorEmbedServer,
|
|
25
|
+
lexicalEditorPopulateServer,
|
|
26
|
+
lexicalEditorToMarkdownServer,
|
|
27
|
+
} from '@byline/richtext-lexical/server'
|
|
28
|
+
import { localStorageProvider } from '@byline/storage-local'
|
|
29
|
+
|
|
30
|
+
import { collections } from './collections/index.js'
|
|
31
|
+
import { serverHooks } from './collections/server-hooks.js'
|
|
32
|
+
import { i18n } from './i18n.js'
|
|
33
|
+
import { DEFAULT_SERVER_URL, routes } from './routes.js'
|
|
34
|
+
|
|
35
|
+
const serverURL = process.env.VITE_SERVER_URL || DEFAULT_SERVER_URL
|
|
36
|
+
|
|
37
|
+
// HMR-safe singleton. Reusing the resolving Promise prevents development
|
|
38
|
+
// reloads from orphaning MySQL pools and exhausting server connections.
|
|
39
|
+
declare global {
|
|
40
|
+
// biome-ignore lint: globalThis augmentation requires `var` rather than `let`
|
|
41
|
+
var __bylineCoreSingleton__: Promise<BylineCore<AdminStore>> | undefined
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
|
|
45
|
+
const db = mysqlAdapter({
|
|
46
|
+
connectionString: process.env.BYLINE_DB_MYSQL_CONNECTION_STRING || '',
|
|
47
|
+
collections,
|
|
48
|
+
defaultContentLocale: i18n.content.defaultLocale,
|
|
49
|
+
// Optional pool tuning. Omit these values to use the adapter defaults:
|
|
50
|
+
// 20 connections, 2s idle timeout, and 30s connection timeout.
|
|
51
|
+
connectionLimit: process.env.BYLINE_DB_MYSQL_CONNECTION_LIMIT
|
|
52
|
+
? Number(process.env.BYLINE_DB_MYSQL_CONNECTION_LIMIT)
|
|
53
|
+
: undefined,
|
|
54
|
+
idleTimeout: process.env.BYLINE_DB_MYSQL_IDLE_TIMEOUT_MILLIS
|
|
55
|
+
? Number(process.env.BYLINE_DB_MYSQL_IDLE_TIMEOUT_MILLIS)
|
|
56
|
+
: undefined,
|
|
57
|
+
connectTimeout: process.env.BYLINE_DB_MYSQL_CONNECTION_TIMEOUT_MILLIS
|
|
58
|
+
? Number(process.env.BYLINE_DB_MYSQL_CONNECTION_TIMEOUT_MILLIS)
|
|
59
|
+
: undefined,
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
const adminStore = createAdminStore(db.drizzle)
|
|
63
|
+
const signingSecret = process.env.BYLINE_JWT_SECRET
|
|
64
|
+
if (!signingSecret || signingSecret.length < 32) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
'BYLINE_JWT_SECRET must be set and carry at least 32 bytes of entropy. ' +
|
|
67
|
+
'Generate one with `openssl rand -base64 48` and add it to your .env.local.'
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const sessionProvider = new JwtSessionProvider({
|
|
72
|
+
store: adminStore,
|
|
73
|
+
signingSecret,
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
registerTanstackStartHostBridge()
|
|
77
|
+
|
|
78
|
+
const core = await initBylineCore<AdminStore>({
|
|
79
|
+
serverURL,
|
|
80
|
+
i18n,
|
|
81
|
+
routes,
|
|
82
|
+
collections,
|
|
83
|
+
hooks: serverHooks,
|
|
84
|
+
db,
|
|
85
|
+
adminStore,
|
|
86
|
+
storage: localStorageProvider({
|
|
87
|
+
uploadDir: './uploads',
|
|
88
|
+
baseUrl: '/uploads',
|
|
89
|
+
}),
|
|
90
|
+
sessionProvider,
|
|
91
|
+
fields: {
|
|
92
|
+
richText: {
|
|
93
|
+
embed: lexicalEditorEmbedServer({ getClient: getAdminBylineClient }),
|
|
94
|
+
populate: lexicalEditorPopulateServer({ getClient: getAdminBylineClient }),
|
|
95
|
+
toMarkdown: lexicalEditorToMarkdownServer(),
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
registerAdminAbilities(core.abilities)
|
|
101
|
+
return core
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
globalThis.__bylineCoreSingleton__ ??= buildBylineCore()
|
|
105
|
+
await globalThis.__bylineCoreSingleton__
|
|
@@ -0,0 +1,115 @@
|
|
|
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
|
+
/**
|
|
10
|
+
* Server-side Byline bootstrap for the MySQL example scaffold.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { type AdminStore, registerAdminAbilities } from '@byline/admin'
|
|
14
|
+
import { JwtSessionProvider } from '@byline/admin/auth'
|
|
15
|
+
import { getAdminBylineClient } from '@byline/client/server'
|
|
16
|
+
import { type BylineCore, initBylineCore } from '@byline/core'
|
|
17
|
+
import { mysqlAdapter } from '@byline/db-mysql'
|
|
18
|
+
import { createAdminStore } from '@byline/db-mysql/admin'
|
|
19
|
+
import { registerTanstackStartHostBridge } from '@byline/host-tanstack-start/integrations/host-bridge'
|
|
20
|
+
import {
|
|
21
|
+
lexicalEditorEmbedServer,
|
|
22
|
+
lexicalEditorPopulateServer,
|
|
23
|
+
lexicalEditorToMarkdownServer,
|
|
24
|
+
lexicalEditorToTextServer,
|
|
25
|
+
} from '@byline/richtext-lexical/server'
|
|
26
|
+
import { migrate, mysqlSearch } from '@byline/search-mysql'
|
|
27
|
+
import { localStorageProvider } from '@byline/storage-local'
|
|
28
|
+
|
|
29
|
+
import { collections } from './collections/index.js'
|
|
30
|
+
import { serverHooks } from './collections/server-hooks.js'
|
|
31
|
+
import { i18n } from './i18n.js'
|
|
32
|
+
import { DEFAULT_SERVER_URL, routes } from './routes.js'
|
|
33
|
+
|
|
34
|
+
const serverURL = process.env.VITE_SERVER_URL || DEFAULT_SERVER_URL
|
|
35
|
+
|
|
36
|
+
// HMR-safe singleton. Reusing the resolving Promise prevents development
|
|
37
|
+
// reloads from orphaning MySQL pools and exhausting server connections.
|
|
38
|
+
declare global {
|
|
39
|
+
// biome-ignore lint: globalThis augmentation requires `var` rather than `let`
|
|
40
|
+
var __bylineCoreSingleton__: Promise<BylineCore<AdminStore>> | undefined
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
|
|
44
|
+
const db = mysqlAdapter({
|
|
45
|
+
connectionString: process.env.BYLINE_DB_MYSQL_CONNECTION_STRING || '',
|
|
46
|
+
collections,
|
|
47
|
+
defaultContentLocale: i18n.content.defaultLocale,
|
|
48
|
+
// Optional pool tuning. Omit these values to use the adapter defaults:
|
|
49
|
+
// 20 connections, 2s idle timeout, and 30s connection timeout.
|
|
50
|
+
connectionLimit: process.env.BYLINE_DB_MYSQL_CONNECTION_LIMIT
|
|
51
|
+
? Number(process.env.BYLINE_DB_MYSQL_CONNECTION_LIMIT)
|
|
52
|
+
: undefined,
|
|
53
|
+
idleTimeout: process.env.BYLINE_DB_MYSQL_IDLE_TIMEOUT_MILLIS
|
|
54
|
+
? Number(process.env.BYLINE_DB_MYSQL_IDLE_TIMEOUT_MILLIS)
|
|
55
|
+
: undefined,
|
|
56
|
+
connectTimeout: process.env.BYLINE_DB_MYSQL_CONNECTION_TIMEOUT_MILLIS
|
|
57
|
+
? Number(process.env.BYLINE_DB_MYSQL_CONNECTION_TIMEOUT_MILLIS)
|
|
58
|
+
: undefined,
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
// Search owns its disposable projection and numbered migration stream.
|
|
62
|
+
// Apply it explicitly at startup with the adapter's existing pool. A
|
|
63
|
+
// migration failure degrades search without taking down the application.
|
|
64
|
+
try {
|
|
65
|
+
await migrate(db.pool, { log: (message) => console.log(message) })
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error('[search-mysql] migrate failed — search may be unavailable:', error)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const adminStore = createAdminStore(db.drizzle)
|
|
71
|
+
const signingSecret = process.env.BYLINE_JWT_SECRET
|
|
72
|
+
if (!signingSecret || signingSecret.length < 32) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
'BYLINE_JWT_SECRET must be set and carry at least 32 bytes of entropy. ' +
|
|
75
|
+
'Generate one with `openssl rand -base64 48` and add it to your .env.local.'
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const sessionProvider = new JwtSessionProvider({
|
|
80
|
+
store: adminStore,
|
|
81
|
+
signingSecret,
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
registerTanstackStartHostBridge()
|
|
85
|
+
|
|
86
|
+
const core = await initBylineCore<AdminStore>({
|
|
87
|
+
serverURL,
|
|
88
|
+
i18n,
|
|
89
|
+
routes,
|
|
90
|
+
collections,
|
|
91
|
+
hooks: serverHooks,
|
|
92
|
+
db,
|
|
93
|
+
adminStore,
|
|
94
|
+
storage: localStorageProvider({
|
|
95
|
+
uploadDir: './uploads',
|
|
96
|
+
baseUrl: '/uploads',
|
|
97
|
+
}),
|
|
98
|
+
sessionProvider,
|
|
99
|
+
fields: {
|
|
100
|
+
richText: {
|
|
101
|
+
embed: lexicalEditorEmbedServer({ getClient: getAdminBylineClient }),
|
|
102
|
+
populate: lexicalEditorPopulateServer({ getClient: getAdminBylineClient }),
|
|
103
|
+
toMarkdown: lexicalEditorToMarkdownServer(),
|
|
104
|
+
toText: lexicalEditorToTextServer(),
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
search: mysqlSearch({ pool: db.pool, defaultLocale: i18n.content.defaultLocale }),
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
registerAdminAbilities(core.abilities)
|
|
111
|
+
return core
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
globalThis.__bylineCoreSingleton__ ??= buildBylineCore()
|
|
115
|
+
await globalThis.__bylineCoreSingleton__
|
|
@@ -44,7 +44,7 @@ const bylineSsrNoExternal = [/^@byline\//]
|
|
|
44
44
|
// kept external so Node resolves them at runtime instead.
|
|
45
45
|
// - sharp + @byline/storage-local — image processing (libvips)
|
|
46
46
|
// - @byline/storage-s3 — bundles the AWS SDK; keep external for Node resolution
|
|
47
|
-
// - @byline/db-postgres
|
|
47
|
+
// - @byline/db-postgres + @byline/db-mysql — database drivers
|
|
48
48
|
// - pino — CJS entrypoints don't execute under Vite's module runner
|
|
49
49
|
//
|
|
50
50
|
// `@byline/admin` is intentionally NOT externalised: it now hosts
|
|
@@ -53,7 +53,7 @@ const bylineSsrNoExternal = [/^@byline\//]
|
|
|
53
53
|
// `import './foo_module.css'`. Node's ESM loader can't handle `.css`,
|
|
54
54
|
// so admin must flow through Vite's SSR pipeline where the CSS plugin
|
|
55
55
|
// resolves the side-effect imports. Server-only admin subpaths reach
|
|
56
|
-
//
|
|
56
|
+
// database / storage adapters through composition at runtime, not
|
|
57
57
|
// through `@byline/admin`'s own import graph, so this is safe.
|
|
58
58
|
const ssrExternal = [
|
|
59
59
|
'sharp',
|
|
@@ -61,6 +61,7 @@ const ssrExternal = [
|
|
|
61
61
|
'@byline/storage-local',
|
|
62
62
|
'@byline/storage-s3',
|
|
63
63
|
'@byline/db-postgres',
|
|
64
|
+
'@byline/db-mysql',
|
|
64
65
|
]
|
|
65
66
|
|
|
66
67
|
const config = defineConfig({
|
|
@@ -198,7 +199,7 @@ const config = defineConfig({
|
|
|
198
199
|
'react-dom',
|
|
199
200
|
'pino',
|
|
200
201
|
'sharp',
|
|
201
|
-
/^@byline\/(db-postgres|storage-local|storage-s3)/,
|
|
202
|
+
/^@byline\/(db-postgres|db-mysql|storage-local|storage-s3)/,
|
|
202
203
|
],
|
|
203
204
|
},
|
|
204
205
|
}),
|