@ossy/app 1.11.9 → 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 CHANGED
@@ -1,97 +1,122 @@
1
1
  # `@ossy/app`
2
2
 
3
- Server-side rendering runtime and build tooling for Ossy apps. Use with `@ossy/cli` for the convention-based setup (`npx @ossy/cli dev`).
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 — see the [root README](../README.md#when-to-use-what).
5
+ For custom setups (Next.js, Vite, etc.), use `@ossy/connected-components` directly.
6
6
 
7
7
  ## Setup
8
8
 
9
- Create `*.page.jsx` files in `src/`:
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
- import React from 'react'
14
- export default () => <h1>Welcome</h1>
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
- Each file becomes a route: `home.page.jsx` → `/`, `about.page.jsx` → `/about`. Optionally export `metadata` for custom id/path or multi-language:
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 = { path: { en: '/about', sv: '/om' } }
21
- export default () => <h1>About</h1>
45
+ export const metadata = {
46
+ id: 'about',
47
+ path: { en: '/about', sv: '/om' },
48
+ }
22
49
  ```
23
50
 
24
- During `dev` / `build`, the tooling writes **JSON manifests** under **`build/.ossy/`**: **`pages.generated.json`** (after compile: route ids, paths, `sourceFile`, merged `metadata`, and **`module`** a `page-modules/<id>.mjs` path the **Node** server `import()`s per request for SSR), **`pages.bundle.json`** (compiled module index), plus the same pattern for API and tasks. **`pages.runtime.mjs`** exports the route table from **`pages.generated.json`** only.
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
- **Client JS (per-page):** For each `*.page.jsx`, the build emits **`build/.ossy/hydrate-<pageId>.jsx`** → **`public/static/<pageId>.js`**. Rollup bundles that entry with the page source so **`react` resolves in the browser** (unlike the Node `page-modules/*.mjs` chunks, which keep `react` external). The HTML only loads the hydrate script for the **current** route. The inline config (`window.__INITIAL_APP_CONFIG__`) keeps request-time fields (theme, `apiUrl`, etc.); `pages` in config include `id`, `path`, and `module` for consistency with the manifest.
59
+ ## Config
27
60
 
28
- Add `src/config.js` for workspace and theme:
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, // or 'light' | 'dark' | CloudDark
36
- apiUrl: 'https://api.ossy.se/api/v0', // optional
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). The server passes `workspaceId`, `apiUrl`, and `theme` to the App component.
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
- Define HTTP handlers as an array of `{ id, path, handle(req, res) }` objects (same shape the server passes to `@ossy/router`).
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
- export default {
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
- API routes are matched before the app is rendered. The router supports dynamic segments (e.g. `path: '/api/users/:id'`); extract params from `req.originalUrl` if needed. Use paths that don't conflict with `/@ossy/*` (reserved for the internal proxy).
86
+ export default function handle(req, res) {
87
+ res.json({ status: 'ok' })
88
+ }
89
+ ```
67
90
 
68
- ## Background worker tasks (`*.task.js`)
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
- For long-running job processors (separate from the SSR server), use **`npx @ossy/cli build --worker`** in a package that only needs the worker. It uses the same Rollup + Babel pipeline as `ossy build`, discovers **`*.task.js`** (and `.task.mjs` / `.task.cjs`) under `src/` (or `--pages <dir>`), and writes **`tasks.generated.json`** / **`tasks.bundle.json`** plus **`tasks.runtime.mjs`**—the same metadata + per-file compile pattern as API routes.
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
- Optional legacy aggregate: **`src/tasks.js`** default export is merged **first**, then each `*.task.js` in path order.
95
+ ## Background tasks (`*.task.js`)
73
96
 
74
- Each task file’s **default export** is `{ type, handler }` where `type` matches the platform job type string, and `handler` is `async ({ sdk, job }) => { ... }` (see `@ossy/worker` for examples).
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
- **Override:** `--task-source ./path/to/tasks.js` skips discovery and uses a single file.
99
+ ```js
100
+ // src/send-email.task.js
101
+ export const metadata = {
102
+ type: 'send-email',
103
+ }
77
104
 
78
- Output: **`build/worker.js`**. Run with `node build/worker.js` (after `import 'dotenv/config'` via the entry). Set `OSSY_WORKSPACE_ID`, `OSSY_API_URL`, and `OSSY_API_TOKEN` as before.
105
+ export default async function handle({ sdk, job }) {
106
+ // process the job
107
+ }
108
+ ```
79
109
 
80
- ## Port configuration
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
- By default, the server listens on port **3000**.
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
- - **Environment variable**: set `PORT`
85
- - **CLI argument**: pass `--port <number>` (or `-p <number>`) when running the built server file
114
+ ## Port configuration
86
115
 
87
- Examples:
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
@@ -199,7 +199,7 @@ export function createOssyAppBundlePlugins ({ nodeEnv }) {
199
199
  babel({
200
200
  babelHelpers: 'bundled',
201
201
  extensions: ['.jsx', '.tsx'],
202
- presets: ['@babel/preset-react'],
202
+ presets: [['@babel/preset-react', { runtime: 'automatic' }]],
203
203
  }),
204
204
  ]
205
205
  }
@@ -226,7 +226,7 @@ export function createOssyClientRollupPlugins ({ nodeEnv, copyPublicFrom, buildP
226
226
  babel({
227
227
  babelHelpers: 'bundled',
228
228
  extensions: ['.jsx', '.tsx'],
229
- presets: ['@babel/preset-react'],
229
+ presets: [['@babel/preset-react', { runtime: 'automatic' }]],
230
230
  }),
231
231
  ]
232
232
  if (copyPublicFrom && fs.existsSync(copyPublicFrom)) {
@@ -261,7 +261,7 @@ export function createOssySsrBundlePlugins ({ nodeEnv }) {
261
261
  babel({
262
262
  babelHelpers: 'bundled',
263
263
  extensions: ['.jsx', '.tsx'],
264
- presets: ['@babel/preset-react'],
264
+ presets: [['@babel/preset-react', { runtime: 'automatic' }]],
265
265
  }),
266
266
  ]
267
267
  }
@@ -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(_page.default, props), {',
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 modules = []
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
- modules.push(`${OSSY_API_MODULES_DIRNAME}/${outName}`)
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 modules
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 modules = []
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
- modules.push(`${OSSY_TASK_MODULES_DIRNAME}/${outName}`)
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 modules
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 raw = JSON.parse(fs.readFileSync(pagesGeneratedPath, 'utf8'))
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, { version: raw.version ?? 1, pages })
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 [, apiModuleList, taskModuleList] = await Promise.all([
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, OSSY_API_BUNDLE_BASENAME), {
624
- version: 1,
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 React, { createElement } from 'react'",
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
- " hydrateRoot(document, createElement(Page, config))",
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 { version: 1, pages }
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 raw = fs.readFileSync(manifestPath, 'utf8')
798
- const data = JSON.parse(raw)
799
- const pages = data?.pages
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
- /** API bundle default may be an empty array. */
16
- const apiRouteList = ApiRoutes ?? []
15
+ const __ossyDir = path.dirname(url.fileURLToPath(import.meta.url)) + '/.ossy'
17
16
 
18
- const sitePageList = Array.isArray(pageRoutes) ? pageRoutes : []
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 && typeof apiRoute.handle === 'function') {
120
+ if (apiRoute?.module) {
117
121
  console.log(`[@ossy/app][server] Handling API route: ${requestUrl}`)
118
- apiRoute.handle(req, res)
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
 
@@ -1,5 +1,10 @@
1
1
  import 'dotenv/config'
2
- import taskHandlers from './.ossy/tasks.runtime.mjs'
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
- runWorkerScheduler(taskHandlers)
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)
@@ -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, handler: (ctx: { sdk: import('@ossy/sdk').SDK, job: Record<string, unknown> }) => Promise<void> }>} handlers
6
+ * @param {Array<{ type: string, module: string }>} tasks
7
+ * @param {string} ossyDir - absolute path to the .ossy directory
5
8
  */
6
- export function runWorkerScheduler(handlers) {
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 handler = handlers.find((h) => h.type === job.type)
99
+ const task = tasks.find((t) => t.type === job.type)
97
100
 
98
- if (!handler) {
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 ${handler.type}`)
106
+ console.log(`Handler found for ${task.type}`)
104
107
 
105
108
  try {
106
- console.log('create sdk')
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
- console.log('created sdk')
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.11.9",
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.11.9",
31
- "@ossy/design-system": "^1.11.9",
32
- "@ossy/pages": "^1.11.9",
33
- "@ossy/router": "^1.11.9",
34
- "@ossy/router-react": "^1.11.9",
35
- "@ossy/sdk": "^1.11.9",
36
- "@ossy/sdk-react": "^1.11.9",
37
- "@ossy/themes": "^1.11.9",
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": "6644afdbe9d5eb8ed1f79657d14b1268ad283bcb"
70
+ "gitHead": "a687fe27dffd3dd2bf152355bbbfd1661b698178"
71
71
  }
@@ -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
@@ -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 : []
@@ -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