@ossy/app 1.12.0 → 1.13.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 +71 -46
- package/cli/build.js +59 -43
- package/cli/server.js +12 -7
- package/cli/worker-entry.js +7 -2
- package/cli/worker-runtime.js +10 -9
- package/package.json +10 -10
- package/cli/api.runtime.mjs +0 -22
- package/cli/pages.runtime.mjs +0 -14
- package/cli/tasks.runtime.mjs +0 -22
package/README.md
CHANGED
|
@@ -1,97 +1,122 @@
|
|
|
1
1
|
# `@ossy/app`
|
|
2
2
|
|
|
3
|
-
Server-side rendering runtime and build tooling for Ossy apps.
|
|
3
|
+
Server-side rendering runtime and build tooling for Ossy apps.
|
|
4
4
|
|
|
5
|
-
For custom setups (Next.js, Vite, etc.), use `@ossy/connected-components` directly
|
|
5
|
+
For custom setups (Next.js, Vite, etc.), use `@ossy/connected-components` directly.
|
|
6
6
|
|
|
7
7
|
## Setup
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Add `@ossy/app`, `react`, `react-dom`, and `@ossy/connected-components` (plus its peer deps) to your `package.json`, then run `app build`.
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "app build",
|
|
15
|
+
"start": "node build/server.js"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Pages
|
|
21
|
+
|
|
22
|
+
Create `*.page.jsx` files in `src/`. Each file becomes a route.
|
|
10
23
|
|
|
11
24
|
```jsx
|
|
12
25
|
// src/home.page.jsx
|
|
13
|
-
|
|
14
|
-
|
|
26
|
+
export const metadata = {
|
|
27
|
+
id: 'home',
|
|
28
|
+
title: 'Home',
|
|
29
|
+
path: '/',
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default function Home({ url }) {
|
|
33
|
+
return (
|
|
34
|
+
<body>
|
|
35
|
+
<h1>Welcome</h1>
|
|
36
|
+
<p>Current URL: {url}</p>
|
|
37
|
+
</body>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
15
40
|
```
|
|
16
41
|
|
|
17
|
-
|
|
42
|
+
**File → route mapping:** `home.page.jsx` → `/`, `about.page.jsx` → `/about`. The `metadata` export controls the route `id`, `path`, and page `title`. For multi-language paths:
|
|
18
43
|
|
|
19
44
|
```js
|
|
20
|
-
export const metadata = {
|
|
21
|
-
|
|
45
|
+
export const metadata = {
|
|
46
|
+
id: 'about',
|
|
47
|
+
path: { en: '/about', sv: '/om' },
|
|
48
|
+
}
|
|
22
49
|
```
|
|
23
50
|
|
|
24
|
-
|
|
51
|
+
**What the framework provides:** The build wraps every page automatically with `<html>`, `<head>` (including the page title and injected styles), and `<App>` (providers for theme, router, SDK). Your page component only needs to return the `<body>` element and its content.
|
|
52
|
+
|
|
53
|
+
**Props:** The full app config is passed as props to the page component — `url`, `theme`, `isAuthenticated`, `pages`, `workspaceId`, `apiUrl`, etc. You can also access config via `useApp()` / `useRouter()` hooks from `@ossy/connected-components` and `@ossy/router-react`.
|
|
54
|
+
|
|
55
|
+
**Build output:** Each page produces two self-contained bundles (React included):
|
|
56
|
+
- `build/ssr/<id>.mjs` — used by the server for SSR on each request
|
|
57
|
+
- `build/public/static/<id>.js` — loaded by the browser for hydration
|
|
25
58
|
|
|
26
|
-
|
|
59
|
+
## Config
|
|
27
60
|
|
|
28
|
-
Add `src/config.js`
|
|
61
|
+
Add `src/config.js` to set workspace, theme, and API options:
|
|
29
62
|
|
|
30
63
|
```js
|
|
31
64
|
import { CloudLight } from '@ossy/themes'
|
|
32
65
|
|
|
33
66
|
export default {
|
|
34
67
|
workspaceId: 'your-workspace-id',
|
|
35
|
-
theme: CloudLight,
|
|
36
|
-
apiUrl: 'https://api.ossy.se/api/v0',
|
|
68
|
+
theme: CloudLight,
|
|
69
|
+
apiUrl: 'https://api.ossy.se/api/v0',
|
|
37
70
|
}
|
|
38
71
|
```
|
|
39
72
|
|
|
40
|
-
Config is loaded at build time and merged with request-time settings (e.g. user theme preference from cookies).
|
|
41
|
-
|
|
42
|
-
Run `npx @ossy/cli dev` or `npx @ossy/cli build`.
|
|
43
|
-
|
|
44
|
-
If the package has **`src/resource-templates/`**, the build also writes **`.ossy-system-templates.generated.js`** there (merging `*.resource.js` into `SystemTemplates`, ordered by filename).
|
|
73
|
+
Config is loaded at build time and merged with request-time settings (e.g. user theme preference from cookies).
|
|
45
74
|
|
|
46
75
|
## API routes
|
|
47
76
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
Add any number of `*.api.js` (or `.api.mjs` / `.api.cjs`) files under `src/` (nested dirs allowed). Each file’s **default export** is either one route object or an array of routes. Files are merged in lexicographic path order.
|
|
51
|
-
|
|
52
|
-
Build/dev writes **`build/.ossy/api.generated.json`** (discovered source paths) and **`api.bundle.json`** (Rollup outputs under **`api-modules/`**). The server imports **`api.runtime.mjs`**, which loads those modules. With no API files, both JSON files list empty arrays.
|
|
53
|
-
|
|
54
|
-
Example `src/health.api.js`:
|
|
77
|
+
Create `*.api.js` files in `src/`. Each file exports a `metadata` object and a default handler function.
|
|
55
78
|
|
|
56
79
|
```js
|
|
57
|
-
|
|
80
|
+
// src/health.api.js
|
|
81
|
+
export const metadata = {
|
|
58
82
|
id: 'health',
|
|
59
83
|
path: '/api/health',
|
|
60
|
-
handle(req, res) {
|
|
61
|
-
res.json({ status: 'ok' })
|
|
62
|
-
},
|
|
63
84
|
}
|
|
64
|
-
```
|
|
65
85
|
|
|
66
|
-
|
|
86
|
+
export default function handle(req, res) {
|
|
87
|
+
res.json({ status: 'ok' })
|
|
88
|
+
}
|
|
89
|
+
```
|
|
67
90
|
|
|
68
|
-
|
|
91
|
+
The handler receives the raw Express `req` and `res`. API routes are matched before page rendering. The router supports dynamic segments (e.g. `path: '/api/users/:id'`).
|
|
69
92
|
|
|
70
|
-
|
|
93
|
+
Build output: `build/.ossy/api.generated.json` — a registry of `[{ id, path, module }]` entries. Handlers are lazy-imported on first request.
|
|
71
94
|
|
|
72
|
-
|
|
95
|
+
## Background tasks (`*.task.js`)
|
|
73
96
|
|
|
74
|
-
|
|
97
|
+
For background job processors, create `*.task.js` files in `src/`. Each file exports a `metadata` object with a `type` field and a default handler function.
|
|
75
98
|
|
|
76
|
-
|
|
99
|
+
```js
|
|
100
|
+
// src/send-email.task.js
|
|
101
|
+
export const metadata = {
|
|
102
|
+
type: 'send-email',
|
|
103
|
+
}
|
|
77
104
|
|
|
78
|
-
|
|
105
|
+
export default async function handle({ sdk, job }) {
|
|
106
|
+
// process the job
|
|
107
|
+
}
|
|
108
|
+
```
|
|
79
109
|
|
|
80
|
-
|
|
110
|
+
The worker polls the job queue every 3 seconds and lazy-imports the handler module the first time a job of that type arrives.
|
|
81
111
|
|
|
82
|
-
|
|
112
|
+
Build output: `build/.ossy/tasks.generated.json` — a registry of `[{ type, module }]` entries. Run the worker with `node build/worker.js`. Requires `OSSY_WORKSPACE_ID`, `OSSY_API_URL`, and `OSSY_API_TOKEN` environment variables.
|
|
83
113
|
|
|
84
|
-
|
|
85
|
-
- **CLI argument**: pass `--port <number>` (or `-p <number>`) when running the built server file
|
|
114
|
+
## Port configuration
|
|
86
115
|
|
|
87
|
-
|
|
116
|
+
The server listens on port **3000** by default.
|
|
88
117
|
|
|
89
118
|
```bash
|
|
90
|
-
# env var
|
|
91
119
|
PORT=4000 node build/server.js
|
|
92
|
-
|
|
93
|
-
# CLI arg
|
|
94
120
|
node build/server.js --port 4000
|
|
95
121
|
node build/server.js -p 4000
|
|
96
122
|
```
|
|
97
|
-
|
package/cli/build.js
CHANGED
|
@@ -275,17 +275,30 @@ export function generatePageSsrModule ({ pageAbsPath, stubAbsPath }) {
|
|
|
275
275
|
"import { createElement } from 'react'",
|
|
276
276
|
"import { renderToPipeableStream } from 'react-dom/server'",
|
|
277
277
|
"import { Writable } from 'node:stream'",
|
|
278
|
+
"import { App } from '@ossy/connected-components'",
|
|
278
279
|
`import * as _page from './${rel}'`,
|
|
279
280
|
'',
|
|
280
281
|
'export const metadata = _page.metadata',
|
|
281
282
|
'',
|
|
283
|
+
'function PageShell (props) {',
|
|
284
|
+
" return createElement('html', { lang: props.defaultLanguage || 'en' },",
|
|
285
|
+
" createElement('head', null,",
|
|
286
|
+
" createElement('meta', { charSet: 'utf-8' }),",
|
|
287
|
+
" createElement('title', null, (_page.metadata && _page.metadata.title) || ''),",
|
|
288
|
+
' ),',
|
|
289
|
+
' createElement(App, props,',
|
|
290
|
+
' createElement(_page.default, props)',
|
|
291
|
+
' )',
|
|
292
|
+
' )',
|
|
293
|
+
'}',
|
|
294
|
+
'',
|
|
282
295
|
'export function renderPage (props, options = {}) {',
|
|
283
296
|
' return new Promise((resolve, reject) => {',
|
|
284
297
|
" let html = ''",
|
|
285
298
|
' const writable = new Writable({',
|
|
286
299
|
' write (chunk, _enc, cb) { html += chunk.toString(); cb() },',
|
|
287
300
|
' })',
|
|
288
|
-
' const { pipe } = renderToPipeableStream(createElement(
|
|
301
|
+
' const { pipe } = renderToPipeableStream(createElement(PageShell, props), {',
|
|
289
302
|
' ...options,',
|
|
290
303
|
' onAllReady () { pipe(writable) },',
|
|
291
304
|
' onError (err) { reject(err) },',
|
|
@@ -395,13 +408,6 @@ export function copyOssyAppRuntime ({ scriptDir, buildPath }) {
|
|
|
395
408
|
fs.copyFileSync(path.join(scriptDir, 'worker-entry.js'), path.join(buildPath, 'worker.js'))
|
|
396
409
|
fs.copyFileSync(path.join(scriptDir, 'worker-runtime.js'), path.join(buildPath, 'worker-runtime.js'))
|
|
397
410
|
const ossyOut = ossyGeneratedDir(buildPath)
|
|
398
|
-
for (const name of [
|
|
399
|
-
OSSY_PAGES_RUNTIME_BASENAME,
|
|
400
|
-
OSSY_API_RUNTIME_BASENAME,
|
|
401
|
-
OSSY_TASKS_RUNTIME_BASENAME,
|
|
402
|
-
]) {
|
|
403
|
-
fs.copyFileSync(path.join(scriptDir, name), path.join(ossyOut, name))
|
|
404
|
-
}
|
|
405
411
|
fs.copyFileSync(
|
|
406
412
|
path.join(scriptDir, OSSY_RENDER_PAGE_RUNTIME_BASENAME),
|
|
407
413
|
path.join(ossyOut, OSSY_RENDER_PAGE_RUNTIME_BASENAME)
|
|
@@ -436,18 +442,12 @@ export function writeOssyJson (filePath, data) {
|
|
|
436
442
|
|
|
437
443
|
/** JSON manifest: discovered API source paths (posix, relative to `cwd`). */
|
|
438
444
|
export function buildApiManifestPayload (apiFiles, cwd = process.cwd()) {
|
|
439
|
-
return
|
|
440
|
-
version: 1,
|
|
441
|
-
files: apiFiles.map((f) => path.relative(cwd, f).replace(/\\/g, '/')),
|
|
442
|
-
}
|
|
445
|
+
return apiFiles.map((f) => path.relative(cwd, f).replace(/\\/g, '/'))
|
|
443
446
|
}
|
|
444
447
|
|
|
445
448
|
/** JSON manifest: discovered task source paths (posix, relative to `cwd`). */
|
|
446
449
|
export function buildTasksManifestPayload (taskFiles, cwd = process.cwd()) {
|
|
447
|
-
return
|
|
448
|
-
version: 1,
|
|
449
|
-
files: taskFiles.map((f) => path.relative(cwd, f).replace(/\\/g, '/')),
|
|
450
|
-
}
|
|
450
|
+
return taskFiles.map((f) => path.relative(cwd, f).replace(/\\/g, '/'))
|
|
451
451
|
}
|
|
452
452
|
|
|
453
453
|
/**
|
|
@@ -520,7 +520,7 @@ export async function compileApiServerModules ({ apiFiles, ossyDir, nodeEnv, onW
|
|
|
520
520
|
return []
|
|
521
521
|
}
|
|
522
522
|
fs.mkdirSync(modsDir, { recursive: true })
|
|
523
|
-
const
|
|
523
|
+
const routes = []
|
|
524
524
|
for (let i = 0; i < apiFiles.length; i++) {
|
|
525
525
|
const outName = `api-${i}.mjs`
|
|
526
526
|
const outFile = path.join(modsDir, outName)
|
|
@@ -530,9 +530,16 @@ export async function compileApiServerModules ({ apiFiles, ossyDir, nodeEnv, onW
|
|
|
530
530
|
nodeEnv,
|
|
531
531
|
onWarn,
|
|
532
532
|
})
|
|
533
|
-
|
|
533
|
+
let meta = {}
|
|
534
|
+
try {
|
|
535
|
+
const mod = await import(pathToFileURL(outFile).href)
|
|
536
|
+
meta = mod?.metadata && typeof mod.metadata === 'object' ? mod.metadata : {}
|
|
537
|
+
} catch {
|
|
538
|
+
// metadata unreadable — skip
|
|
539
|
+
}
|
|
540
|
+
routes.push({ ...meta, module: `${OSSY_API_MODULES_DIRNAME}/${outName}` })
|
|
534
541
|
}
|
|
535
|
-
return
|
|
542
|
+
return routes
|
|
536
543
|
}
|
|
537
544
|
|
|
538
545
|
export async function compileTaskServerModules ({ taskFiles, ossyDir, nodeEnv, onWarn }) {
|
|
@@ -542,7 +549,7 @@ export async function compileTaskServerModules ({ taskFiles, ossyDir, nodeEnv, o
|
|
|
542
549
|
return []
|
|
543
550
|
}
|
|
544
551
|
fs.mkdirSync(modsDir, { recursive: true })
|
|
545
|
-
const
|
|
552
|
+
const tasks = []
|
|
546
553
|
for (let i = 0; i < taskFiles.length; i++) {
|
|
547
554
|
const outName = `task-${i}.mjs`
|
|
548
555
|
const outFile = path.join(modsDir, outName)
|
|
@@ -552,9 +559,16 @@ export async function compileTaskServerModules ({ taskFiles, ossyDir, nodeEnv, o
|
|
|
552
559
|
nodeEnv,
|
|
553
560
|
onWarn,
|
|
554
561
|
})
|
|
555
|
-
|
|
562
|
+
let meta = {}
|
|
563
|
+
try {
|
|
564
|
+
const mod = await import(pathToFileURL(outFile).href)
|
|
565
|
+
meta = mod?.metadata && typeof mod.metadata === 'object' ? mod.metadata : {}
|
|
566
|
+
} catch {
|
|
567
|
+
// metadata unreadable — skip
|
|
568
|
+
}
|
|
569
|
+
tasks.push({ ...meta, module: `${OSSY_TASK_MODULES_DIRNAME}/${outName}` })
|
|
556
570
|
}
|
|
557
|
-
return
|
|
571
|
+
return tasks
|
|
558
572
|
}
|
|
559
573
|
|
|
560
574
|
/**
|
|
@@ -567,8 +581,7 @@ export async function compileTaskServerModules ({ taskFiles, ossyDir, nodeEnv, o
|
|
|
567
581
|
*/
|
|
568
582
|
export async function enrichPagesGeneratedManifest ({ ossyDir, pagesGeneratedPath }) {
|
|
569
583
|
if (!fs.existsSync(pagesGeneratedPath)) return
|
|
570
|
-
const
|
|
571
|
-
const basePages = raw?.pages
|
|
584
|
+
const basePages = JSON.parse(fs.readFileSync(pagesGeneratedPath, 'utf8'))
|
|
572
585
|
if (!Array.isArray(basePages) || basePages.length === 0) return
|
|
573
586
|
|
|
574
587
|
const pages = []
|
|
@@ -599,7 +612,7 @@ export async function enrichPagesGeneratedManifest ({ ossyDir, pagesGeneratedPat
|
|
|
599
612
|
}
|
|
600
613
|
pages.push(merged)
|
|
601
614
|
}
|
|
602
|
-
writeOssyJson(pagesGeneratedPath,
|
|
615
|
+
writeOssyJson(pagesGeneratedPath, pages)
|
|
603
616
|
}
|
|
604
617
|
|
|
605
618
|
/**
|
|
@@ -615,19 +628,13 @@ export async function compileOssyNodeArtifacts ({
|
|
|
615
628
|
nodeEnv,
|
|
616
629
|
onWarn,
|
|
617
630
|
}) {
|
|
618
|
-
const [,
|
|
631
|
+
const [, apiRouteList, taskList] = await Promise.all([
|
|
619
632
|
compilePageSsrModules({ pageFiles, srcDir, ossyDir, buildPath, nodeEnv, onWarn }),
|
|
620
633
|
compileApiServerModules({ apiFiles, ossyDir, nodeEnv, onWarn }),
|
|
621
634
|
compileTaskServerModules({ taskFiles, ossyDir, nodeEnv, onWarn }),
|
|
622
635
|
])
|
|
623
|
-
writeOssyJson(path.join(ossyDir,
|
|
624
|
-
|
|
625
|
-
modules: apiModuleList,
|
|
626
|
-
})
|
|
627
|
-
writeOssyJson(path.join(ossyDir, OSSY_TASKS_BUNDLE_BASENAME), {
|
|
628
|
-
version: 1,
|
|
629
|
-
modules: taskModuleList,
|
|
630
|
-
})
|
|
636
|
+
writeOssyJson(path.join(ossyDir, OSSY_GEN_API_BASENAME), apiRouteList)
|
|
637
|
+
writeOssyJson(path.join(ossyDir, OSSY_GEN_TASKS_BASENAME), taskList)
|
|
631
638
|
await enrichPagesGeneratedManifest({
|
|
632
639
|
ossyDir,
|
|
633
640
|
pagesGeneratedPath: path.join(ossyDir, OSSY_GEN_PAGES_BASENAME),
|
|
@@ -710,17 +717,28 @@ export function generatePageHydrateModule ({ pageAbsPath, stubAbsPath, srcDir })
|
|
|
710
717
|
return [
|
|
711
718
|
'// Generated by @ossy/app — do not edit',
|
|
712
719
|
'',
|
|
713
|
-
"import
|
|
720
|
+
"import { createElement } from 'react'",
|
|
714
721
|
"import { hydrateRoot } from 'react-dom/client'",
|
|
722
|
+
"import { App } from '@ossy/connected-components'",
|
|
715
723
|
`import * as _page from './${rel}'`,
|
|
716
724
|
'',
|
|
717
725
|
'export default _page.default',
|
|
718
726
|
'export const metadata = _page.metadata',
|
|
719
727
|
'',
|
|
720
728
|
"if (typeof window !== 'undefined') {",
|
|
721
|
-
" const Page = _page.default",
|
|
722
729
|
" const config = window.__INITIAL_APP_CONFIG__ || {}",
|
|
723
|
-
|
|
730
|
+
' function PageShell (props) {',
|
|
731
|
+
" return createElement('html', { lang: props.defaultLanguage || 'en' },",
|
|
732
|
+
" createElement('head', null,",
|
|
733
|
+
" createElement('meta', { charSet: 'utf-8' }),",
|
|
734
|
+
" createElement('title', null, (_page.metadata && _page.metadata.title) || ''),",
|
|
735
|
+
' ),',
|
|
736
|
+
' createElement(App, props,',
|
|
737
|
+
' createElement(_page.default, props)',
|
|
738
|
+
' )',
|
|
739
|
+
' )',
|
|
740
|
+
' }',
|
|
741
|
+
' hydrateRoot(document, createElement(PageShell, config))',
|
|
724
742
|
'}',
|
|
725
743
|
'',
|
|
726
744
|
].join('\n')
|
|
@@ -780,7 +798,7 @@ export function buildPagesGeneratedPayload (pageFiles, srcDir, cwd = process.cwd
|
|
|
780
798
|
sourceFile: path.relative(cwd, f).replace(/\\/g, '/'),
|
|
781
799
|
}
|
|
782
800
|
})
|
|
783
|
-
return
|
|
801
|
+
return pages
|
|
784
802
|
}
|
|
785
803
|
|
|
786
804
|
export function writePagesManifest ({
|
|
@@ -794,11 +812,9 @@ export function writePagesManifest ({
|
|
|
794
812
|
|
|
795
813
|
export function parsePagesFromManifestJson (manifestPath) {
|
|
796
814
|
try {
|
|
797
|
-
const
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
if (!Array.isArray(pages)) return []
|
|
801
|
-
return pages.map((p) => ({
|
|
815
|
+
const data = JSON.parse(fs.readFileSync(manifestPath, 'utf8'))
|
|
816
|
+
if (!Array.isArray(data)) return []
|
|
817
|
+
return data.map((p) => ({
|
|
802
818
|
id: p.id,
|
|
803
819
|
path: p.path,
|
|
804
820
|
...(typeof p.module === 'string' ? { module: p.module } : {}),
|
package/cli/server.js
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
1
2
|
import path from 'path';
|
|
2
3
|
import url from 'url'
|
|
4
|
+
import { pathToFileURL } from 'node:url'
|
|
3
5
|
import express from 'express'
|
|
4
6
|
import morgan from 'morgan'
|
|
5
7
|
import { Router as OssyRouter } from '@ossy/router'
|
|
6
8
|
import { ProxyInternal } from './proxy-internal.js'
|
|
7
9
|
import cookieParser from 'cookie-parser'
|
|
8
10
|
|
|
9
|
-
import ApiRoutes from './.ossy/api.runtime.mjs'
|
|
10
|
-
import pageRoutes from './.ossy/pages.runtime.mjs'
|
|
11
11
|
import buildTimeConfig from './.ossy/server-config.runtime.mjs'
|
|
12
12
|
import { BuildPage, buildPrerenderAppConfig } from './.ossy/render-page.task.js'
|
|
13
13
|
import Middleware from './.ossy/middleware.runtime.js'
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
const apiRouteList = ApiRoutes ?? []
|
|
15
|
+
const __ossyDir = path.dirname(url.fileURLToPath(import.meta.url)) + '/.ossy'
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
function readOssyJson (name) {
|
|
18
|
+
return JSON.parse(fs.readFileSync(path.join(__ossyDir, name), 'utf8'))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const apiRouteList = readOssyJson('api.generated.json') ?? []
|
|
22
|
+
const sitePageList = readOssyJson('pages.generated.json') ?? []
|
|
19
23
|
|
|
20
24
|
/** When `src/config.js` is minimal, infer language list from the first multi-path page. */
|
|
21
25
|
function pageRouterLanguageOptions (config, pages) {
|
|
@@ -113,9 +117,10 @@ app.all('*all', async (req, res) => {
|
|
|
113
117
|
|
|
114
118
|
try {
|
|
115
119
|
const apiRoute = apiRouter.getPageByUrl(requestUrl)
|
|
116
|
-
if (apiRoute
|
|
120
|
+
if (apiRoute?.module) {
|
|
117
121
|
console.log(`[@ossy/app][server] Handling API route: ${requestUrl}`)
|
|
118
|
-
|
|
122
|
+
const mod = await import(pathToFileURL(path.resolve(__ossyDir, apiRoute.module)).href)
|
|
123
|
+
await mod.default(req, res)
|
|
119
124
|
return
|
|
120
125
|
}
|
|
121
126
|
|
package/cli/worker-entry.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import 'dotenv/config'
|
|
2
|
-
import
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
3
5
|
import { runWorkerScheduler } from './worker-runtime.js'
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
const __ossyDir = path.dirname(fileURLToPath(import.meta.url)) + '/.ossy'
|
|
8
|
+
const tasks = JSON.parse(fs.readFileSync(path.join(__ossyDir, 'tasks.generated.json'), 'utf8')) ?? []
|
|
9
|
+
|
|
10
|
+
runWorkerScheduler(tasks, __ossyDir)
|
package/cli/worker-runtime.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import { pathToFileURL } from 'node:url'
|
|
1
3
|
import { SDK } from '@ossy/sdk'
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
|
-
* @param {Array<{ type: string,
|
|
6
|
+
* @param {Array<{ type: string, module: string }>} tasks
|
|
7
|
+
* @param {string} ossyDir - absolute path to the .ossy directory
|
|
5
8
|
*/
|
|
6
|
-
export function runWorkerScheduler(
|
|
9
|
+
export function runWorkerScheduler(tasks, ossyDir) {
|
|
7
10
|
const sdk = SDK.of({
|
|
8
11
|
workspaceId: process.env.OSSY_WORKSPACE_ID,
|
|
9
12
|
apiUrl: process.env.OSSY_API_URL,
|
|
@@ -93,24 +96,22 @@ export function runWorkerScheduler(handlers) {
|
|
|
93
96
|
console.log(`Processing ${jobList.length} jobs`)
|
|
94
97
|
for (const job of jobList) {
|
|
95
98
|
console.log(`Processing job ${job.id}`)
|
|
96
|
-
const
|
|
99
|
+
const task = tasks.find((t) => t.type === job.type)
|
|
97
100
|
|
|
98
|
-
if (!
|
|
101
|
+
if (!task) {
|
|
99
102
|
console.log('No handler found for job', job.id)
|
|
100
103
|
continue
|
|
101
104
|
}
|
|
102
105
|
|
|
103
|
-
console.log(`Handler found for ${
|
|
106
|
+
console.log(`Handler found for ${task.type}`)
|
|
104
107
|
|
|
105
108
|
try {
|
|
106
|
-
|
|
109
|
+
const mod = await import(pathToFileURL(path.resolve(ossyDir, task.module)).href)
|
|
107
110
|
const jobSdk = SDK.of({
|
|
108
111
|
workspaceId: job.belongsTo,
|
|
109
112
|
authorization: process.env.OSSY_API_TOKEN,
|
|
110
113
|
})
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
await handler.handler({ sdk: jobSdk, job }).catch(() => {})
|
|
114
|
+
await mod.default({ sdk: jobSdk, job }).catch(() => {})
|
|
114
115
|
} catch (error) {
|
|
115
116
|
console.error(error)
|
|
116
117
|
console.log('Failed to processing job')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ossy/app",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"source": "./src/index.js",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -27,14 +27,14 @@
|
|
|
27
27
|
"@babel/eslint-parser": "^7.15.8",
|
|
28
28
|
"@babel/preset-react": "^7.26.3",
|
|
29
29
|
"@babel/register": "^7.25.9",
|
|
30
|
-
"@ossy/connected-components": "^1.
|
|
31
|
-
"@ossy/design-system": "^1.
|
|
32
|
-
"@ossy/pages": "^1.
|
|
33
|
-
"@ossy/router": "^1.
|
|
34
|
-
"@ossy/router-react": "^1.
|
|
35
|
-
"@ossy/sdk": "^1.
|
|
36
|
-
"@ossy/sdk-react": "^1.
|
|
37
|
-
"@ossy/themes": "^1.
|
|
30
|
+
"@ossy/connected-components": "^1.13.0",
|
|
31
|
+
"@ossy/design-system": "^1.13.0",
|
|
32
|
+
"@ossy/pages": "^1.13.0",
|
|
33
|
+
"@ossy/router": "^1.13.0",
|
|
34
|
+
"@ossy/router-react": "^1.13.0",
|
|
35
|
+
"@ossy/sdk": "^1.13.0",
|
|
36
|
+
"@ossy/sdk-react": "^1.13.0",
|
|
37
|
+
"@ossy/themes": "^1.13.0",
|
|
38
38
|
"@rollup/plugin-alias": "^6.0.0",
|
|
39
39
|
"@rollup/plugin-babel": "6.1.0",
|
|
40
40
|
"@rollup/plugin-commonjs": "^29.0.0",
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
"README.md",
|
|
68
68
|
"tsconfig.json"
|
|
69
69
|
],
|
|
70
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "a687fe27dffd3dd2bf152355bbbfd1661b698178"
|
|
71
71
|
}
|
package/cli/api.runtime.mjs
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
4
|
-
|
|
5
|
-
const __ossyDir = path.dirname(fileURLToPath(import.meta.url))
|
|
6
|
-
|
|
7
|
-
function normalizeApiExport (mod) {
|
|
8
|
-
const d = mod?.default
|
|
9
|
-
if (d == null) return []
|
|
10
|
-
return Array.isArray(d) ? d : [d]
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const { modules } = JSON.parse(fs.readFileSync(path.join(__ossyDir, 'api.bundle.json'), 'utf8'))
|
|
14
|
-
|
|
15
|
-
const out = []
|
|
16
|
-
for (const rel of modules) {
|
|
17
|
-
const abs = path.resolve(__ossyDir, rel)
|
|
18
|
-
const mod = await import(pathToFileURL(abs).href)
|
|
19
|
-
out.push(...normalizeApiExport(mod))
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export default out
|
package/cli/pages.runtime.mjs
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import { fileURLToPath } from 'node:url'
|
|
4
|
-
|
|
5
|
-
const __ossyDir = path.dirname(fileURLToPath(import.meta.url))
|
|
6
|
-
|
|
7
|
-
function readJson (name) {
|
|
8
|
-
return JSON.parse(fs.readFileSync(path.join(__ossyDir, name), 'utf8'))
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/** Route list from `pages.generated.json` (includes `module` for lazy `import()` after build). */
|
|
12
|
-
const { pages } = readJson('pages.generated.json')
|
|
13
|
-
|
|
14
|
-
export default Array.isArray(pages) ? pages : []
|
package/cli/tasks.runtime.mjs
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
4
|
-
|
|
5
|
-
const __ossyDir = path.dirname(fileURLToPath(import.meta.url))
|
|
6
|
-
|
|
7
|
-
function normalizeTaskExport (mod) {
|
|
8
|
-
const d = mod?.default
|
|
9
|
-
if (d == null) return []
|
|
10
|
-
return Array.isArray(d) ? d : [d]
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const { modules } = JSON.parse(fs.readFileSync(path.join(__ossyDir, 'tasks.bundle.json'), 'utf8'))
|
|
14
|
-
|
|
15
|
-
const out = []
|
|
16
|
-
for (const rel of modules) {
|
|
17
|
-
const abs = path.resolve(__ossyDir, rel)
|
|
18
|
-
const mod = await import(pathToFileURL(abs).href)
|
|
19
|
-
out.push(...normalizeTaskExport(mod))
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export default out
|