@ossy/app 1.40.2 → 3.0.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 +49 -12
- package/cli/build.task.js +49 -10
- package/cli/get-platform-files.task.js +21 -15
- package/cli/manifest-plugin.js +340 -48
- package/package.json +25 -13
- package/runtime/merge-shell-slots.js +148 -0
- package/runtime/page-runtime.js +36 -18
- package/runtime/resolve-app-slots.js +126 -0
- package/src/en.translations.json +8 -0
- package/src/manifest/action-id-to-tool-name.js +29 -0
- package/src/manifest/build-action-input-schema.js +220 -0
- package/src/manifest/build-actions-schema.js +36 -0
- package/src/manifest/build-capabilities.js +190 -0
- package/src/manifest/build-manifest-summary.js +11 -5
- package/src/manifest/extract-task-catalog.js +1 -0
- package/src/manifest/resolve-page-layout.js +51 -0
- package/src/manifest/serialize-package-definition.js +2 -3
- package/src/manifest/template-to-json-schema.js +103 -0
- package/src/shell/App.jsx +18 -9
- package/src/shell/AppSettings.jsx +17 -0
- package/src/shell/DevPagesPanel.jsx +17 -13
- package/src/shell/HeaderAuthActions.jsx +49 -0
- package/src/shell/LanguageList.jsx +83 -0
- package/src/shell/LanguageSelect.jsx +73 -0
- package/src/shell/ThemeEditor.jsx +19 -34
- package/src/shell/ThemeSelect.jsx +101 -0
- package/src/shell/WorkspaceAppSettingsSync.jsx +39 -0
- package/src/shell/buildSidebarNav.js +113 -0
- package/src/shell/index.js +11 -0
- package/src/shell/languageCode.js +31 -0
- package/src/shell/patchUserAppSettings.js +10 -0
- package/src/shell/resolveEndpoints.js +43 -0
- package/src/shell/resolveWorkspaceServices.js +20 -0
- package/src/shell/themeEditorStyles.js +49 -0
- package/src/shell/useCompactShellLayout.js +24 -0
- package/src/shell/useShellWorkspace.js +40 -0
- package/src/shell-registry/blank.layout.jsx +8 -0
- package/src/shell-registry/default.layout.jsx +101 -0
- package/src/shell-registry/footer-default.component.jsx +39 -0
- package/src/shell-registry/head-default.component.jsx +17 -0
- package/src/shell-registry/header-default.component.jsx +50 -0
- package/src/shell-registry/logo-logomark.component.jsx +8 -0
- package/src/shell-registry/logo-logotype.component.jsx +15 -0
- package/src/shell-registry/minimal.layout.jsx +58 -0
- package/src/shell-registry/sidebar-default.component.jsx +281 -0
- package/src/sv.translations.json +8 -0
- package/runtime/resolve-shell-slots.js +0 -112
package/README.md
CHANGED
|
@@ -70,6 +70,12 @@ export default {
|
|
|
70
70
|
}
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
Optional flags:
|
|
74
|
+
|
|
75
|
+
| Key | Default | Purpose |
|
|
76
|
+
|-----|---------|---------|
|
|
77
|
+
| `enablePushInvalidation` | `false` | App-wide SSE cache invalidation. Prefer `<PushInvalidationSubscriber />` on specific pages instead — see [@ossy/sdk-react README](../sdk-react/README.md#push-invalidation). |
|
|
78
|
+
|
|
73
79
|
Config is loaded at build time and merged with request-time settings (e.g. user theme preference from cookies).
|
|
74
80
|
|
|
75
81
|
## API routes
|
|
@@ -94,40 +100,71 @@ Build output: `build/.ossy/api.generated.json` — a registry of `[{ id, path, m
|
|
|
94
100
|
|
|
95
101
|
## Background tasks (`*.task.js`)
|
|
96
102
|
|
|
97
|
-
|
|
103
|
+
Task modules export `metadata` (id, optional `triggers` / `schedule`) and a `run` function. They are registered by `TaskService` at server startup.
|
|
104
|
+
|
|
105
|
+
**Sync invoke:** `POST /actions` with `{ action, payload }` awaits the primary task at `{feature}/tasks/{intent}` derived from the action id.
|
|
106
|
+
|
|
107
|
+
**Async:** changestream events and cron schedules dispatch matching tasks without blocking the caller.
|
|
108
|
+
|
|
109
|
+
Build output includes task entries in the manifest for server registration. Async execution does **not** use a separate polling worker — see ADR 0003.
|
|
110
|
+
|
|
111
|
+
Task ids use the canonical form `@ossy/{feature}/tasks/{intent}`. Action ids (`@ossy/{feature}/actions/{intent}`) map to their primary task via ADR 0003.
|
|
112
|
+
|
|
113
|
+
## Schemas (`*.schema.js`)
|
|
114
|
+
|
|
115
|
+
Resource document schemas are POJOs default-exported from `*.schema.js` in `src/` or installed feature packages. The build discovers them, validates canonical ids (`@{provider}/{feature}/schema/{concept}`), and merges them into `manifest.schemas`.
|
|
98
116
|
|
|
99
117
|
```js
|
|
100
|
-
// src/
|
|
101
|
-
export
|
|
102
|
-
|
|
118
|
+
// packages/booking/src/service.schema.js
|
|
119
|
+
export default {
|
|
120
|
+
id: '@ossy/booking/schema/service',
|
|
121
|
+
name: 'Service',
|
|
122
|
+
fields: [{ name: 'name', type: 'text', required: true }],
|
|
103
123
|
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Forms reference a schema via `metadata.schemaId` (not `templateId`).
|
|
127
|
+
|
|
128
|
+
## Forms (`*.form.js`)
|
|
129
|
+
|
|
130
|
+
Form modules export intent metadata only — field definitions live on the schema:
|
|
104
131
|
|
|
105
|
-
|
|
106
|
-
|
|
132
|
+
```js
|
|
133
|
+
export const metadata = {
|
|
134
|
+
id: '@ossy/booking/form/service',
|
|
135
|
+
schemaId: '@ossy/booking/schema/service',
|
|
107
136
|
}
|
|
108
137
|
```
|
|
109
138
|
|
|
110
|
-
|
|
139
|
+
## Build manifest
|
|
140
|
+
|
|
141
|
+
`app build` writes `build/manifest.json` plus derived catalogs:
|
|
142
|
+
|
|
143
|
+
| Output | Purpose |
|
|
144
|
+
|--------|---------|
|
|
145
|
+
| `manifest.json` | Pages, actions, tasks, `schemas`, components, layouts, `taskCatalog`, `taskGraphEdges`, … |
|
|
146
|
+
| `build/capabilities.json` | Automation UI catalog — actions, tasks, schemas, edges |
|
|
147
|
+
| `build/actions.schema.json` | JSON Schema for agent/MCP tool inputs per action |
|
|
111
148
|
|
|
112
|
-
|
|
149
|
+
Action and task metadata are validated at build time via `@ossy/schema`. Task triggers reference schema ids (`type: '@ossy/platform/schema/file'`) and lifecycle events (`Created`, `Patched`, …).
|
|
113
150
|
|
|
114
151
|
## Components (`*.component.jsx`)
|
|
115
152
|
|
|
116
153
|
Register injectable UI via `*.component.jsx` in `src/` or feature packages. Components are bundled separately and resolved into slots at runtime.
|
|
117
154
|
|
|
118
|
-
**
|
|
155
|
+
**App chrome** — the app assigns placement in `*.layout.jsx`:
|
|
119
156
|
|
|
120
157
|
```jsx
|
|
121
158
|
export const slots = {
|
|
122
|
-
'
|
|
123
|
-
'
|
|
159
|
+
'app:header': 'app-header',
|
|
160
|
+
'app:sidebar': 'app-sidebar',
|
|
124
161
|
}
|
|
125
162
|
```
|
|
126
163
|
|
|
127
164
|
**Resource / input UI** — feature packages use `metadata.id` as the slot key:
|
|
128
165
|
|
|
129
166
|
```jsx
|
|
130
|
-
export const metadata = { id: '
|
|
167
|
+
export const metadata = { id: '@ossy/booking/form/service' }
|
|
131
168
|
export default function ServiceForm() { … }
|
|
132
169
|
```
|
|
133
170
|
|
package/cli/build.task.js
CHANGED
|
@@ -13,7 +13,7 @@ import getPlatformFiles, {
|
|
|
13
13
|
PAGE_FILE_PATTERN,
|
|
14
14
|
API_FILE_PATTERN,
|
|
15
15
|
TASK_FILE_PATTERN,
|
|
16
|
-
|
|
16
|
+
SCHEMA_FILE_PATTERN,
|
|
17
17
|
COMPONENT_FILE_PATTERN,
|
|
18
18
|
AGGREGATE_FILE_PATTERN,
|
|
19
19
|
INTEGRATION_FILE_PATTERN,
|
|
@@ -21,13 +21,14 @@ import getPlatformFiles, {
|
|
|
21
21
|
EMAIL_FILE_PATTERN,
|
|
22
22
|
ACTION_FILE_PATTERN,
|
|
23
23
|
E2E_FILE_PATTERN,
|
|
24
|
+
FLOW_FILE_PATTERN,
|
|
24
25
|
LAYOUT_FILE_PATTERN,
|
|
25
26
|
TRANSLATIONS_FILE_PATTERN,
|
|
26
27
|
} from './get-platform-files.task.js'
|
|
27
28
|
import { manifestPlugin } from './manifest-plugin.js'
|
|
28
29
|
import { mergeAndEmitTranslations } from './merge-translations.task.js'
|
|
29
30
|
|
|
30
|
-
export { PAGE_FILE_PATTERN, API_FILE_PATTERN, TASK_FILE_PATTERN,
|
|
31
|
+
export { PAGE_FILE_PATTERN, API_FILE_PATTERN, TASK_FILE_PATTERN, SCHEMA_FILE_PATTERN, COMPONENT_FILE_PATTERN, AGGREGATE_FILE_PATTERN, INTEGRATION_FILE_PATTERN, STARTUP_FILE_PATTERN, EMAIL_FILE_PATTERN, ACTION_FILE_PATTERN, E2E_FILE_PATTERN, FLOW_FILE_PATTERN, LAYOUT_FILE_PATTERN, TRANSLATIONS_FILE_PATTERN }
|
|
31
32
|
|
|
32
33
|
// Generated entry stubs live under `build/.ossy/entries/`. Putting them
|
|
33
34
|
// inside `build/` means they're cleaned automatically with every build,
|
|
@@ -126,10 +127,8 @@ function generateTaskStub ({ stubAbs, sourceAbs }) {
|
|
|
126
127
|
].join('\n')
|
|
127
128
|
}
|
|
128
129
|
|
|
129
|
-
//
|
|
130
|
-
|
|
131
|
-
// can read it via the same `await import(chunkUrl)` path as the other kinds.
|
|
132
|
-
function generateResourceStub ({ stubAbs, sourceAbs }) {
|
|
130
|
+
// Schemas are pure data (POJOs default-exported from `*.schema.js`).
|
|
131
|
+
function generateSchemaStub ({ stubAbs, sourceAbs }) {
|
|
133
132
|
const importPath = relImport(stubAbs, sourceAbs)
|
|
134
133
|
return [
|
|
135
134
|
'// Generated by @ossy/app — do not edit',
|
|
@@ -166,7 +165,22 @@ function generateAggregateStub ({ stubAbs, sourceAbs }) {
|
|
|
166
165
|
// Integrations are server-side only. The stub re-exports `id`, `credentials`,
|
|
167
166
|
// and `connect` so the manifest plugin can validate them and the platform
|
|
168
167
|
// server can call `connect({ env })` at startup.
|
|
169
|
-
|
|
168
|
+
//
|
|
169
|
+
// grafana-metrics mutates a package singleton (`metrics`). A relative re-export
|
|
170
|
+
// gets bundled into its own copy, so TaskService recordings never reach
|
|
171
|
+
// connect() — re-export via @ossy/observability to keep one module instance.
|
|
172
|
+
function generateIntegrationStub ({ stubAbs, sourceAbs, packageName }) {
|
|
173
|
+
const base = path.basename(sourceAbs)
|
|
174
|
+
if (packageName === '@ossy/observability' && base === 'grafana-metrics.integration.js') {
|
|
175
|
+
return [
|
|
176
|
+
'// Generated by @ossy/app — do not edit',
|
|
177
|
+
"import { grafanaMetricsIntegration } from '@ossy/observability'",
|
|
178
|
+
'export const id = grafanaMetricsIntegration.id',
|
|
179
|
+
'export const credentials = grafanaMetricsIntegration.credentials',
|
|
180
|
+
'export const connect = grafanaMetricsIntegration.connect',
|
|
181
|
+
'',
|
|
182
|
+
].join('\n')
|
|
183
|
+
}
|
|
170
184
|
const importPath = relImport(stubAbs, sourceAbs)
|
|
171
185
|
return [
|
|
172
186
|
'// Generated by @ossy/app — do not edit',
|
|
@@ -208,6 +222,16 @@ function generateActionStub ({ stubAbs, sourceAbs }) {
|
|
|
208
222
|
].join('\n')
|
|
209
223
|
}
|
|
210
224
|
|
|
225
|
+
// Forms are intent metadata (schemaId reference). Field schema lives on the resource.
|
|
226
|
+
function generateFormStub ({ stubAbs, sourceAbs }) {
|
|
227
|
+
const importPath = relImport(stubAbs, sourceAbs)
|
|
228
|
+
return [
|
|
229
|
+
'// Generated by @ossy/app — do not edit',
|
|
230
|
+
`export { metadata } from '${importPath}'`,
|
|
231
|
+
'',
|
|
232
|
+
].join('\n')
|
|
233
|
+
}
|
|
234
|
+
|
|
211
235
|
// E2e tests are Playwright test modules. The stub re-exports `id`, `feature`,
|
|
212
236
|
// `requires`, and the default test function so the manifest plugin can validate
|
|
213
237
|
// them and the e2e runner can import and execute them.
|
|
@@ -220,6 +244,15 @@ function generateE2eStub ({ stubAbs, sourceAbs }) {
|
|
|
220
244
|
].join('\n')
|
|
221
245
|
}
|
|
222
246
|
|
|
247
|
+
function generateFlowStub ({ stubAbs, sourceAbs }) {
|
|
248
|
+
const importPath = relImport(stubAbs, sourceAbs)
|
|
249
|
+
return [
|
|
250
|
+
'// Generated by @ossy/app — do not edit',
|
|
251
|
+
`export { metadata, default } from '${importPath}'`,
|
|
252
|
+
'',
|
|
253
|
+
].join('\n')
|
|
254
|
+
}
|
|
255
|
+
|
|
223
256
|
// Layouts are React components that wrap page renders. The stub re-exports
|
|
224
257
|
// everything (including required `id`) plus the
|
|
225
258
|
// default component so the manifest plugin can validate them and the server
|
|
@@ -237,14 +270,16 @@ function generateLayoutStub ({ stubAbs, sourceAbs }) {
|
|
|
237
270
|
function stubFor (kind, args) {
|
|
238
271
|
if (kind === 'page') return generatePageStub(args)
|
|
239
272
|
if (kind === 'api') return generateApiStub(args)
|
|
240
|
-
if (kind === '
|
|
273
|
+
if (kind === 'schema') return generateSchemaStub(args)
|
|
241
274
|
if (kind === 'component') return generateComponentStub(args)
|
|
242
275
|
if (kind === 'aggregate') return generateAggregateStub(args)
|
|
243
276
|
if (kind === 'integration') return generateIntegrationStub(args)
|
|
244
277
|
if (kind === 'startup') return generateStartupStub(args)
|
|
245
278
|
if (kind === 'email') return generateEmailStub(args)
|
|
246
279
|
if (kind === 'action') return generateActionStub(args)
|
|
280
|
+
if (kind === 'form') return generateFormStub(args)
|
|
247
281
|
if (kind === 'e2e') return generateE2eStub(args)
|
|
282
|
+
if (kind === 'flow') return generateFlowStub(args)
|
|
248
283
|
if (kind === 'layout') return generateLayoutStub(args)
|
|
249
284
|
return generateTaskStub(args)
|
|
250
285
|
}
|
|
@@ -294,14 +329,16 @@ export async function build (cliArgs = []) {
|
|
|
294
329
|
...platformFiles.pages,
|
|
295
330
|
...platformFiles.apis,
|
|
296
331
|
...platformFiles.tasks,
|
|
297
|
-
...platformFiles.
|
|
332
|
+
...platformFiles.schemas,
|
|
298
333
|
...platformFiles.components,
|
|
299
334
|
...platformFiles.aggregates,
|
|
300
335
|
...platformFiles.integrations,
|
|
301
336
|
...platformFiles.startups,
|
|
302
337
|
...platformFiles.emails,
|
|
303
338
|
...platformFiles.actions,
|
|
339
|
+
...platformFiles.forms,
|
|
304
340
|
...platformFiles.e2es,
|
|
341
|
+
...platformFiles.flows,
|
|
305
342
|
...platformFiles.layouts,
|
|
306
343
|
]
|
|
307
344
|
|
|
@@ -313,7 +350,7 @@ export async function build (cliArgs = []) {
|
|
|
313
350
|
const effectiveSrcDir = packageSrcDir || srcDir
|
|
314
351
|
const inputName = entryNameFromSource(sourcePath, effectiveSrcDir, packageName)
|
|
315
352
|
const stubAbs = path.join(stubDir, stubFileNameFor(kind, sourcePath, effectiveSrcDir, packageName))
|
|
316
|
-
fs.writeFileSync(stubAbs, stubFor(kind, { stubAbs, sourceAbs: sourcePath }), 'utf8')
|
|
353
|
+
fs.writeFileSync(stubAbs, stubFor(kind, { stubAbs, sourceAbs: sourcePath, packageName, packageSrcDir }), 'utf8')
|
|
317
354
|
stubInputMap[inputName] = stubAbs
|
|
318
355
|
entriesByStub.set(stubAbs, { kind, sourcePath, packageName, packageSrcDir })
|
|
319
356
|
}
|
|
@@ -350,6 +387,8 @@ export async function build (cliArgs = []) {
|
|
|
350
387
|
if (id === '@ossy/platform' || id.startsWith('@ossy/platform/')) return true
|
|
351
388
|
if (id === '@ossy/event-store' || id.startsWith('@ossy/event-store/')) return true
|
|
352
389
|
if (id === '@ossy/observability' || id.startsWith('@ossy/observability/')) return true
|
|
390
|
+
// Shared in-memory outbox — email integration appends, dev-inbox API reads.
|
|
391
|
+
if (id === '@ossy/email/local-outbox') return true
|
|
353
392
|
return false
|
|
354
393
|
},
|
|
355
394
|
plugins: [
|
|
@@ -4,19 +4,21 @@ import path from 'node:path'
|
|
|
4
4
|
export const PAGE_FILE_PATTERN = /\.page\.(jsx?|tsx?)$/
|
|
5
5
|
export const API_FILE_PATTERN = /\.api\.(mjs|cjs|js)$/
|
|
6
6
|
export const TASK_FILE_PATTERN = /\.task\.(mjs|cjs|js)$/
|
|
7
|
-
export const
|
|
7
|
+
export const SCHEMA_FILE_PATTERN = /\.schema\.(mjs|cjs|js)$/
|
|
8
8
|
export const COMPONENT_FILE_PATTERN = /\.component\.(jsx?|tsx?)$/
|
|
9
9
|
export const AGGREGATE_FILE_PATTERN = /\.aggregate\.(mjs|cjs|js)$/
|
|
10
10
|
export const INTEGRATION_FILE_PATTERN = /\.integration\.(mjs|cjs|js)$/
|
|
11
11
|
export const STARTUP_FILE_PATTERN = /\.startup\.(mjs|cjs|js)$/
|
|
12
12
|
export const EMAIL_FILE_PATTERN = /\.email\.(jsx?|tsx?)$/
|
|
13
13
|
export const ACTION_FILE_PATTERN = /\.action\.(mjs|cjs|js)$/
|
|
14
|
+
export const FORM_FILE_PATTERN = /\.form\.(mjs|cjs|js)$/
|
|
14
15
|
export const E2E_FILE_PATTERN = /\.e2e\.(mjs|cjs|js)$/
|
|
16
|
+
export const FLOW_FILE_PATTERN = /\.flow\.(mjs|cjs|js)$/
|
|
15
17
|
export const LAYOUT_FILE_PATTERN = /\.layout\.(jsx?|tsx?)$/
|
|
16
18
|
export const TRANSLATIONS_FILE_PATTERN = /\.translations\.json$/
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
|
-
* @typedef {'page' | 'api' | 'task' | '
|
|
21
|
+
* @typedef {'page' | 'api' | 'task' | 'schema' | 'component' | 'aggregate' | 'integration' | 'startup' | 'email' | 'action' | 'form' | 'e2e' | 'flow' | 'layout'} EntryKind
|
|
20
22
|
*
|
|
21
23
|
* @typedef {object} PlatformEntry
|
|
22
24
|
* @property {EntryKind} kind Which bucket this entry lives in.
|
|
@@ -28,15 +30,17 @@ export const TRANSLATIONS_FILE_PATTERN = /\.translations\.json$/
|
|
|
28
30
|
* @property {PlatformEntry[]} pages Discovered `*.page.{jsx,tsx,...}` entries.
|
|
29
31
|
* @property {PlatformEntry[]} apis Discovered `*.api.{js,mjs,cjs}` entries.
|
|
30
32
|
* @property {PlatformEntry[]} tasks Discovered `*.task.{js,mjs,cjs}` entries.
|
|
31
|
-
* @property {PlatformEntry[]}
|
|
33
|
+
* @property {PlatformEntry[]} schemas Discovered `*.schema.{js,mjs,cjs}` entries (ADR 0006).
|
|
32
34
|
* @property {PlatformEntry[]} components Discovered `*.component.{jsx,tsx,...}` entries.
|
|
33
35
|
* @property {PlatformEntry[]} aggregates Discovered `*.aggregate.{js,mjs,cjs}` entries (installed packages only).
|
|
34
36
|
* @property {PlatformEntry[]} integrations Discovered `*.integration.{js,mjs,cjs}` entries.
|
|
35
37
|
* @property {PlatformEntry[]} startups Discovered `*.startup.{js,mjs,cjs}` entries.
|
|
36
38
|
* @property {PlatformEntry[]} emails Discovered `*.email.{jsx,tsx,...}` entries.
|
|
37
39
|
* @property {PlatformEntry[]} actions Discovered `*.action.{js,mjs,cjs}` entries.
|
|
40
|
+
* @property {PlatformEntry[]} forms Discovered `*.form.{js,mjs,cjs}` entries.
|
|
38
41
|
* @property {PlatformEntry[]} e2es Discovered `*.e2e.{js,mjs,cjs}` entries (installed packages only).
|
|
39
|
-
* @property {PlatformEntry[]}
|
|
42
|
+
* @property {PlatformEntry[]} flows Discovered `*.flow.{js,mjs,cjs}` entries (installed packages only).
|
|
43
|
+
* @property {PlatformEntry[]} layouts Discovered `*.layout.{jsx,tsx,...}` entries (app and installed packages).
|
|
40
44
|
*/
|
|
41
45
|
export function discoverFilesByPattern (srcDir, filePattern) {
|
|
42
46
|
const dir = path.resolve(srcDir)
|
|
@@ -58,14 +62,16 @@ function classifyFile (absPath) {
|
|
|
58
62
|
if (PAGE_FILE_PATTERN.test(base)) return 'page'
|
|
59
63
|
if (API_FILE_PATTERN.test(base)) return 'api'
|
|
60
64
|
if (TASK_FILE_PATTERN.test(base)) return 'task'
|
|
61
|
-
if (
|
|
65
|
+
if (SCHEMA_FILE_PATTERN.test(base)) return 'schema'
|
|
62
66
|
if (COMPONENT_FILE_PATTERN.test(base)) return 'component'
|
|
63
67
|
if (AGGREGATE_FILE_PATTERN.test(base)) return 'aggregate'
|
|
64
68
|
if (INTEGRATION_FILE_PATTERN.test(base)) return 'integration'
|
|
65
69
|
if (STARTUP_FILE_PATTERN.test(base)) return 'startup'
|
|
66
70
|
if (EMAIL_FILE_PATTERN.test(base)) return 'email'
|
|
67
71
|
if (ACTION_FILE_PATTERN.test(base)) return 'action'
|
|
72
|
+
if (FORM_FILE_PATTERN.test(base)) return 'form'
|
|
68
73
|
if (E2E_FILE_PATTERN.test(base)) return 'e2e'
|
|
74
|
+
if (FLOW_FILE_PATTERN.test(base)) return 'flow'
|
|
69
75
|
if (LAYOUT_FILE_PATTERN.test(base)) return 'layout'
|
|
70
76
|
return null
|
|
71
77
|
}
|
|
@@ -167,16 +173,17 @@ export function discoverInstalledPackageEntries (projectRoot) {
|
|
|
167
173
|
...discoverFilesByPattern(packageSrcDir, PAGE_FILE_PATTERN),
|
|
168
174
|
...discoverFilesByPattern(packageSrcDir, API_FILE_PATTERN),
|
|
169
175
|
...discoverFilesByPattern(packageSrcDir, TASK_FILE_PATTERN),
|
|
170
|
-
...discoverFilesByPattern(packageSrcDir,
|
|
176
|
+
...discoverFilesByPattern(packageSrcDir, SCHEMA_FILE_PATTERN),
|
|
171
177
|
...discoverFilesByPattern(packageSrcDir, COMPONENT_FILE_PATTERN),
|
|
172
178
|
...discoverFilesByPattern(packageSrcDir, AGGREGATE_FILE_PATTERN),
|
|
173
179
|
...discoverFilesByPattern(packageSrcDir, INTEGRATION_FILE_PATTERN),
|
|
174
180
|
...discoverFilesByPattern(packageSrcDir, STARTUP_FILE_PATTERN),
|
|
175
181
|
...discoverFilesByPattern(packageSrcDir, EMAIL_FILE_PATTERN),
|
|
176
182
|
...discoverFilesByPattern(packageSrcDir, ACTION_FILE_PATTERN),
|
|
183
|
+
...discoverFilesByPattern(packageSrcDir, FORM_FILE_PATTERN),
|
|
177
184
|
...discoverFilesByPattern(packageSrcDir, E2E_FILE_PATTERN),
|
|
178
|
-
|
|
179
|
-
|
|
185
|
+
...discoverFilesByPattern(packageSrcDir, FLOW_FILE_PATTERN),
|
|
186
|
+
...discoverFilesByPattern(packageSrcDir, LAYOUT_FILE_PATTERN),
|
|
180
187
|
]
|
|
181
188
|
for (const sourcePath of files) {
|
|
182
189
|
const stat = fs.statSync(sourcePath)
|
|
@@ -222,21 +229,20 @@ export function discoverInstalledPackageEntries (projectRoot) {
|
|
|
222
229
|
* @returns {Promise<PlatformFiles>}
|
|
223
230
|
*/
|
|
224
231
|
export default async function getPlatformFiles (srcDir) {
|
|
225
|
-
const out = { pages: [], apis: [], tasks: [],
|
|
226
|
-
const bucketByKind = { page: 'pages', api: 'apis', task: 'tasks',
|
|
232
|
+
const out = { pages: [], apis: [], tasks: [], schemas: [], components: [], aggregates: [], integrations: [], startups: [], emails: [], actions: [], forms: [], e2es: [], flows: [], layouts: [] }
|
|
233
|
+
const bucketByKind = { page: 'pages', api: 'apis', task: 'tasks', schema: 'schemas', component: 'components', aggregate: 'aggregates', integration: 'integrations', startup: 'startups', email: 'emails', action: 'actions', form: 'forms', e2e: 'e2es', flow: 'flows', layout: 'layouts' }
|
|
227
234
|
|
|
228
235
|
// Aggregates and e2e tests are only discovered from installed packages — never
|
|
229
236
|
// from the local `src/` — to avoid pulling in per-project duplicates of the
|
|
230
237
|
// same aggregate class or test suite that is canonically owned by the feature
|
|
231
238
|
// package.
|
|
232
|
-
// Layouts are
|
|
233
|
-
// app
|
|
234
|
-
// content (pages, actions, tasks, etc.) and must never define layouts.
|
|
239
|
+
// Layouts are discovered from the app's `src/` and from installed packages
|
|
240
|
+
// (`@ossy/app` ships default layouts via `ossy.src`).
|
|
235
241
|
const localFiles = [
|
|
236
242
|
...discoverFilesByPattern(srcDir, PAGE_FILE_PATTERN),
|
|
237
243
|
...discoverFilesByPattern(srcDir, API_FILE_PATTERN),
|
|
238
244
|
...discoverFilesByPattern(srcDir, TASK_FILE_PATTERN),
|
|
239
|
-
...discoverFilesByPattern(srcDir,
|
|
245
|
+
...discoverFilesByPattern(srcDir, SCHEMA_FILE_PATTERN),
|
|
240
246
|
...discoverFilesByPattern(srcDir, COMPONENT_FILE_PATTERN),
|
|
241
247
|
...discoverFilesByPattern(srcDir, INTEGRATION_FILE_PATTERN),
|
|
242
248
|
...discoverFilesByPattern(srcDir, STARTUP_FILE_PATTERN),
|
|
@@ -248,7 +254,7 @@ export default async function getPlatformFiles (srcDir) {
|
|
|
248
254
|
const stat = fs.statSync(sourcePath)
|
|
249
255
|
if (!stat.isFile() || stat.size === 0) continue
|
|
250
256
|
const kind = classifyFile(sourcePath)
|
|
251
|
-
if (!kind || kind === 'aggregate' || kind === 'e2e') continue
|
|
257
|
+
if (!kind || kind === 'aggregate' || kind === 'e2e' || kind === 'flow') continue
|
|
252
258
|
out[bucketByKind[kind]].push({ kind, sourcePath })
|
|
253
259
|
}
|
|
254
260
|
|