@ossy/app 1.12.0 → 1.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -46
- package/cli/build.js +60 -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) },',
|
|
@@ -306,6 +319,7 @@ export function writePageSsrStubs (pageFiles, srcDir, ossyDir) {
|
|
|
306
319
|
for (const f of pageFiles) {
|
|
307
320
|
const pageId = clientHydrateIdForPage(f, srcDir)
|
|
308
321
|
const stubPath = path.join(entriesDir, `${pageId}.mjs`)
|
|
322
|
+
fs.mkdirSync(path.dirname(stubPath), { recursive: true })
|
|
309
323
|
fs.writeFileSync(stubPath, generatePageSsrModule({ pageAbsPath: f, stubAbsPath: stubPath }))
|
|
310
324
|
}
|
|
311
325
|
}
|
|
@@ -395,13 +409,6 @@ export function copyOssyAppRuntime ({ scriptDir, buildPath }) {
|
|
|
395
409
|
fs.copyFileSync(path.join(scriptDir, 'worker-entry.js'), path.join(buildPath, 'worker.js'))
|
|
396
410
|
fs.copyFileSync(path.join(scriptDir, 'worker-runtime.js'), path.join(buildPath, 'worker-runtime.js'))
|
|
397
411
|
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
412
|
fs.copyFileSync(
|
|
406
413
|
path.join(scriptDir, OSSY_RENDER_PAGE_RUNTIME_BASENAME),
|
|
407
414
|
path.join(ossyOut, OSSY_RENDER_PAGE_RUNTIME_BASENAME)
|
|
@@ -436,18 +443,12 @@ export function writeOssyJson (filePath, data) {
|
|
|
436
443
|
|
|
437
444
|
/** JSON manifest: discovered API source paths (posix, relative to `cwd`). */
|
|
438
445
|
export function buildApiManifestPayload (apiFiles, cwd = process.cwd()) {
|
|
439
|
-
return
|
|
440
|
-
version: 1,
|
|
441
|
-
files: apiFiles.map((f) => path.relative(cwd, f).replace(/\\/g, '/')),
|
|
442
|
-
}
|
|
446
|
+
return apiFiles.map((f) => path.relative(cwd, f).replace(/\\/g, '/'))
|
|
443
447
|
}
|
|
444
448
|
|
|
445
449
|
/** JSON manifest: discovered task source paths (posix, relative to `cwd`). */
|
|
446
450
|
export function buildTasksManifestPayload (taskFiles, cwd = process.cwd()) {
|
|
447
|
-
return
|
|
448
|
-
version: 1,
|
|
449
|
-
files: taskFiles.map((f) => path.relative(cwd, f).replace(/\\/g, '/')),
|
|
450
|
-
}
|
|
451
|
+
return taskFiles.map((f) => path.relative(cwd, f).replace(/\\/g, '/'))
|
|
451
452
|
}
|
|
452
453
|
|
|
453
454
|
/**
|
|
@@ -520,7 +521,7 @@ export async function compileApiServerModules ({ apiFiles, ossyDir, nodeEnv, onW
|
|
|
520
521
|
return []
|
|
521
522
|
}
|
|
522
523
|
fs.mkdirSync(modsDir, { recursive: true })
|
|
523
|
-
const
|
|
524
|
+
const routes = []
|
|
524
525
|
for (let i = 0; i < apiFiles.length; i++) {
|
|
525
526
|
const outName = `api-${i}.mjs`
|
|
526
527
|
const outFile = path.join(modsDir, outName)
|
|
@@ -530,9 +531,16 @@ export async function compileApiServerModules ({ apiFiles, ossyDir, nodeEnv, onW
|
|
|
530
531
|
nodeEnv,
|
|
531
532
|
onWarn,
|
|
532
533
|
})
|
|
533
|
-
|
|
534
|
+
let meta = {}
|
|
535
|
+
try {
|
|
536
|
+
const mod = await import(pathToFileURL(outFile).href)
|
|
537
|
+
meta = mod?.metadata && typeof mod.metadata === 'object' ? mod.metadata : {}
|
|
538
|
+
} catch {
|
|
539
|
+
// metadata unreadable — skip
|
|
540
|
+
}
|
|
541
|
+
routes.push({ ...meta, module: `${OSSY_API_MODULES_DIRNAME}/${outName}` })
|
|
534
542
|
}
|
|
535
|
-
return
|
|
543
|
+
return routes
|
|
536
544
|
}
|
|
537
545
|
|
|
538
546
|
export async function compileTaskServerModules ({ taskFiles, ossyDir, nodeEnv, onWarn }) {
|
|
@@ -542,7 +550,7 @@ export async function compileTaskServerModules ({ taskFiles, ossyDir, nodeEnv, o
|
|
|
542
550
|
return []
|
|
543
551
|
}
|
|
544
552
|
fs.mkdirSync(modsDir, { recursive: true })
|
|
545
|
-
const
|
|
553
|
+
const tasks = []
|
|
546
554
|
for (let i = 0; i < taskFiles.length; i++) {
|
|
547
555
|
const outName = `task-${i}.mjs`
|
|
548
556
|
const outFile = path.join(modsDir, outName)
|
|
@@ -552,9 +560,16 @@ export async function compileTaskServerModules ({ taskFiles, ossyDir, nodeEnv, o
|
|
|
552
560
|
nodeEnv,
|
|
553
561
|
onWarn,
|
|
554
562
|
})
|
|
555
|
-
|
|
563
|
+
let meta = {}
|
|
564
|
+
try {
|
|
565
|
+
const mod = await import(pathToFileURL(outFile).href)
|
|
566
|
+
meta = mod?.metadata && typeof mod.metadata === 'object' ? mod.metadata : {}
|
|
567
|
+
} catch {
|
|
568
|
+
// metadata unreadable — skip
|
|
569
|
+
}
|
|
570
|
+
tasks.push({ ...meta, module: `${OSSY_TASK_MODULES_DIRNAME}/${outName}` })
|
|
556
571
|
}
|
|
557
|
-
return
|
|
572
|
+
return tasks
|
|
558
573
|
}
|
|
559
574
|
|
|
560
575
|
/**
|
|
@@ -567,8 +582,7 @@ export async function compileTaskServerModules ({ taskFiles, ossyDir, nodeEnv, o
|
|
|
567
582
|
*/
|
|
568
583
|
export async function enrichPagesGeneratedManifest ({ ossyDir, pagesGeneratedPath }) {
|
|
569
584
|
if (!fs.existsSync(pagesGeneratedPath)) return
|
|
570
|
-
const
|
|
571
|
-
const basePages = raw?.pages
|
|
585
|
+
const basePages = JSON.parse(fs.readFileSync(pagesGeneratedPath, 'utf8'))
|
|
572
586
|
if (!Array.isArray(basePages) || basePages.length === 0) return
|
|
573
587
|
|
|
574
588
|
const pages = []
|
|
@@ -599,7 +613,7 @@ export async function enrichPagesGeneratedManifest ({ ossyDir, pagesGeneratedPat
|
|
|
599
613
|
}
|
|
600
614
|
pages.push(merged)
|
|
601
615
|
}
|
|
602
|
-
writeOssyJson(pagesGeneratedPath,
|
|
616
|
+
writeOssyJson(pagesGeneratedPath, pages)
|
|
603
617
|
}
|
|
604
618
|
|
|
605
619
|
/**
|
|
@@ -615,19 +629,13 @@ export async function compileOssyNodeArtifacts ({
|
|
|
615
629
|
nodeEnv,
|
|
616
630
|
onWarn,
|
|
617
631
|
}) {
|
|
618
|
-
const [,
|
|
632
|
+
const [, apiRouteList, taskList] = await Promise.all([
|
|
619
633
|
compilePageSsrModules({ pageFiles, srcDir, ossyDir, buildPath, nodeEnv, onWarn }),
|
|
620
634
|
compileApiServerModules({ apiFiles, ossyDir, nodeEnv, onWarn }),
|
|
621
635
|
compileTaskServerModules({ taskFiles, ossyDir, nodeEnv, onWarn }),
|
|
622
636
|
])
|
|
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
|
-
})
|
|
637
|
+
writeOssyJson(path.join(ossyDir, OSSY_GEN_API_BASENAME), apiRouteList)
|
|
638
|
+
writeOssyJson(path.join(ossyDir, OSSY_GEN_TASKS_BASENAME), taskList)
|
|
631
639
|
await enrichPagesGeneratedManifest({
|
|
632
640
|
ossyDir,
|
|
633
641
|
pagesGeneratedPath: path.join(ossyDir, OSSY_GEN_PAGES_BASENAME),
|
|
@@ -710,17 +718,28 @@ export function generatePageHydrateModule ({ pageAbsPath, stubAbsPath, srcDir })
|
|
|
710
718
|
return [
|
|
711
719
|
'// Generated by @ossy/app — do not edit',
|
|
712
720
|
'',
|
|
713
|
-
"import
|
|
721
|
+
"import { createElement } from 'react'",
|
|
714
722
|
"import { hydrateRoot } from 'react-dom/client'",
|
|
723
|
+
"import { App } from '@ossy/connected-components'",
|
|
715
724
|
`import * as _page from './${rel}'`,
|
|
716
725
|
'',
|
|
717
726
|
'export default _page.default',
|
|
718
727
|
'export const metadata = _page.metadata',
|
|
719
728
|
'',
|
|
720
729
|
"if (typeof window !== 'undefined') {",
|
|
721
|
-
" const Page = _page.default",
|
|
722
730
|
" const config = window.__INITIAL_APP_CONFIG__ || {}",
|
|
723
|
-
|
|
731
|
+
' function PageShell (props) {',
|
|
732
|
+
" return createElement('html', { lang: props.defaultLanguage || 'en' },",
|
|
733
|
+
" createElement('head', null,",
|
|
734
|
+
" createElement('meta', { charSet: 'utf-8' }),",
|
|
735
|
+
" createElement('title', null, (_page.metadata && _page.metadata.title) || ''),",
|
|
736
|
+
' ),',
|
|
737
|
+
' createElement(App, props,',
|
|
738
|
+
' createElement(_page.default, props)',
|
|
739
|
+
' )',
|
|
740
|
+
' )',
|
|
741
|
+
' }',
|
|
742
|
+
' hydrateRoot(document, createElement(PageShell, config))',
|
|
724
743
|
'}',
|
|
725
744
|
'',
|
|
726
745
|
].join('\n')
|
|
@@ -780,7 +799,7 @@ export function buildPagesGeneratedPayload (pageFiles, srcDir, cwd = process.cwd
|
|
|
780
799
|
sourceFile: path.relative(cwd, f).replace(/\\/g, '/'),
|
|
781
800
|
}
|
|
782
801
|
})
|
|
783
|
-
return
|
|
802
|
+
return pages
|
|
784
803
|
}
|
|
785
804
|
|
|
786
805
|
export function writePagesManifest ({
|
|
@@ -794,11 +813,9 @@ export function writePagesManifest ({
|
|
|
794
813
|
|
|
795
814
|
export function parsePagesFromManifestJson (manifestPath) {
|
|
796
815
|
try {
|
|
797
|
-
const
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
if (!Array.isArray(pages)) return []
|
|
801
|
-
return pages.map((p) => ({
|
|
816
|
+
const data = JSON.parse(fs.readFileSync(manifestPath, 'utf8'))
|
|
817
|
+
if (!Array.isArray(data)) return []
|
|
818
|
+
return data.map((p) => ({
|
|
802
819
|
id: p.id,
|
|
803
820
|
path: p.path,
|
|
804
821
|
...(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.1",
|
|
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.1",
|
|
31
|
+
"@ossy/design-system": "^1.13.1",
|
|
32
|
+
"@ossy/pages": "^1.13.1",
|
|
33
|
+
"@ossy/router": "^1.13.1",
|
|
34
|
+
"@ossy/router-react": "^1.13.1",
|
|
35
|
+
"@ossy/sdk": "^1.13.1",
|
|
36
|
+
"@ossy/sdk-react": "^1.13.1",
|
|
37
|
+
"@ossy/themes": "^1.13.1",
|
|
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": "3dcfb2cb94eb920a9cb90f266904bd165772269e"
|
|
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
|