@powerhousedao/vetra 6.0.2-staging.9 → 6.1.0-dev.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/dist/app-module-D_cAVvt0.js +48 -0
- package/dist/app-module-D_cAVvt0.js.map +1 -0
- package/dist/codegen/index.d.ts +117 -0
- package/dist/codegen/index.d.ts.map +1 -0
- package/dist/codegen/index.js +793 -0
- package/dist/codegen/index.js.map +1 -0
- package/dist/{connect-CTIucT0o.js → connect-BRlVKs1s.js} +1 -1
- package/dist/{connect-CTIucT0o.js.map → connect-BRlVKs1s.js.map} +1 -1
- package/dist/document-editor-1NFr9t3a.js +48 -0
- package/dist/document-editor-1NFr9t3a.js.map +1 -0
- package/dist/document-models/app-module/index.d.ts +1 -1
- package/dist/document-models/app-module/index.js +4 -2
- package/dist/document-models/document-editor/index.d.ts +1 -1
- package/dist/document-models/document-editor/index.js +4 -2
- package/dist/document-models/index.d.ts +5 -5
- package/dist/document-models/index.js +5 -5
- package/dist/document-models/processor-module/index.d.ts +1 -1
- package/dist/document-models/processor-module/index.js +4 -2
- package/dist/document-models/subgraph-module/index.d.ts +1 -1
- package/dist/document-models/subgraph-module/index.js +4 -2
- package/dist/document-models/vetra-package/index.d.ts +1 -1
- package/dist/document-models/vetra-package/index.js +2 -2
- package/dist/{editor-CbtrPBTP.js → editor-BwbJ65NZ.js} +4 -4
- package/dist/{editor-CbtrPBTP.js.map → editor-BwbJ65NZ.js.map} +1 -1
- package/dist/{editor-BSQfwcdz.js → editor-CJAn9WTA.js} +4 -4
- package/dist/{editor-BSQfwcdz.js.map → editor-CJAn9WTA.js.map} +1 -1
- package/dist/{editor-D70FYIwV.js → editor-CLERZjRS.js} +1 -1
- package/dist/{editor-D70FYIwV.js.map → editor-CLERZjRS.js.map} +1 -1
- package/dist/{editor-Dtg8STyT.js → editor-DWufJcmG.js} +4 -4
- package/dist/{editor-Dtg8STyT.js.map → editor-DWufJcmG.js.map} +1 -1
- package/dist/{editor-SfVorwsP.js → editor-DabfMxkF.js} +4 -4
- package/dist/{editor-SfVorwsP.js.map → editor-DabfMxkF.js.map} +1 -1
- package/dist/{editor-Bku8e2dr.js → editor-WWMGVelM.js} +4 -4
- package/dist/{editor-Bku8e2dr.js.map → editor-WWMGVelM.js.map} +1 -1
- package/dist/editors/hooks/index.js +1 -1
- package/dist/editors/index.js +1 -1
- package/dist/{factory-D8ZDOMP3.js → factory-Bd_0OaF8.js} +2 -2
- package/dist/{factory-D8ZDOMP3.js.map → factory-Bd_0OaF8.js.map} +1 -1
- package/dist/{factory-CweZppg6.js → factory-CEzh-u5J.js} +7 -7
- package/dist/factory-CEzh-u5J.js.map +1 -0
- package/dist/{hooks-Xexr33XS.js → hooks-Btogj1f0.js} +1 -1
- package/dist/{hooks-Xexr33XS.js.map → hooks-Btogj1f0.js.map} +1 -1
- package/dist/{module-BYEvqA47.d.ts → index-BPoF8P3O.d.ts} +57 -57
- package/dist/index-BPoF8P3O.d.ts.map +1 -0
- package/dist/{module-CpP8D2Hl.d.ts → index-BoGed4Fa.d.ts} +88 -88
- package/dist/index-BoGed4Fa.d.ts.map +1 -0
- package/dist/{index-BfK0fsx4.d.ts → index-C4s0aLv3.d.ts} +49 -49
- package/dist/index-C4s0aLv3.d.ts.map +1 -0
- package/dist/{index-BdmD5tsW.d.ts → index-ClaOdn_F.d.ts} +46 -46
- package/dist/index-ClaOdn_F.d.ts.map +1 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +6 -6
- package/dist/{logger-BLNtquAC.js → logger-CYkStoEb.js} +1 -1
- package/dist/{logger-BLNtquAC.js.map → logger-CYkStoEb.js.map} +1 -1
- package/dist/module-DPyV2vUY.js +138 -0
- package/dist/module-DPyV2vUY.js.map +1 -0
- package/dist/{module-CCqD26Hf.d.ts → module-Drla4Vmn.d.ts} +1 -1
- package/dist/{module-CCqD26Hf.d.ts.map → module-Drla4Vmn.d.ts.map} +1 -1
- package/dist/{module-CZLiUtDI.js → module-dDKBr6QY.js} +1 -1
- package/dist/{module-CZLiUtDI.js.map → module-dDKBr6QY.js.map} +1 -1
- package/dist/{module-CXIFrq-R.js → module-jWN6gsmi.js} +7 -7
- package/dist/{module-CXIFrq-R.js.map → module-jWN6gsmi.js.map} +1 -1
- package/dist/module-pK0hpO6c.js +131 -0
- package/dist/module-pK0hpO6c.js.map +1 -0
- package/dist/module-ryHOwQp-.js +105 -0
- package/dist/module-ryHOwQp-.js.map +1 -0
- package/dist/module-x2Liyk0-.js +80 -0
- package/dist/module-x2Liyk0-.js.map +1 -0
- package/dist/ph-factories-B0_E6LY6.js +53 -0
- package/dist/ph-factories-B0_E6LY6.js.map +1 -0
- package/dist/ph-factories-B6PSPkPQ.js +52 -0
- package/dist/ph-factories-B6PSPkPQ.js.map +1 -0
- package/dist/ph-factories-Bn_HTUfT.js +50 -0
- package/dist/ph-factories-Bn_HTUfT.js.map +1 -0
- package/dist/ph-factories-CJXfT_Z1.js +51 -0
- package/dist/ph-factories-CJXfT_Z1.js.map +1 -0
- package/dist/processor-module-B7IAItXQ.js +48 -0
- package/dist/processor-module-B7IAItXQ.js.map +1 -0
- package/dist/processors/codegen/index.d.ts +3 -21305
- package/dist/processors/codegen/index.d.ts.map +1 -1
- package/dist/processors/codegen/index.js +1 -1
- package/dist/processors/index.js +1 -1
- package/dist/processors/vetra-read-model/index.js +1 -1
- package/dist/subgraph-module-CfzSZTNC.js +48 -0
- package/dist/subgraph-module-CfzSZTNC.js.map +1 -0
- package/dist/switchboard-CIUZkhea.js +8 -0
- package/dist/{switchboard-JOTFVXuj.js.map → switchboard-CIUZkhea.js.map} +1 -1
- package/dist/{useVetraDocument-DoAwvHVi.js → useVetraDocument-BQKyels4.js} +1 -1
- package/dist/{useVetraDocument-DoAwvHVi.js.map → useVetraDocument-BQKyels4.js.map} +1 -1
- package/dist/{module-CTPfENDU.js → utils-BRx88WW3.js} +5 -130
- package/dist/utils-BRx88WW3.js.map +1 -0
- package/dist/{module-BWlCj_0C.js → utils-Byjf33qt.js} +5 -137
- package/dist/utils-Byjf33qt.js.map +1 -0
- package/dist/{module-BoGgbKmO.js → utils-CWO5kDus.js} +5 -79
- package/dist/utils-CWO5kDus.js.map +1 -0
- package/dist/{module-D0SK3Nds.js → utils-CnZc-0E4.js} +5 -104
- package/dist/utils-CnZc-0E4.js.map +1 -0
- package/dist/{vetra-package-YNLxEA-U.js → vetra-package-BgTjv343.js} +2 -2
- package/dist/{vetra-package-YNLxEA-U.js.map → vetra-package-BgTjv343.js.map} +1 -1
- package/package.json +12 -8
- package/dist/app-module-Bbj7PI-S.js +0 -94
- package/dist/app-module-Bbj7PI-S.js.map +0 -1
- package/dist/document-editor-DS-bZMKx.js +0 -93
- package/dist/document-editor-DS-bZMKx.js.map +0 -1
- package/dist/factory-CweZppg6.js.map +0 -1
- package/dist/index-BdmD5tsW.d.ts.map +0 -1
- package/dist/index-BfK0fsx4.d.ts.map +0 -1
- package/dist/module-BWlCj_0C.js.map +0 -1
- package/dist/module-BYEvqA47.d.ts.map +0 -1
- package/dist/module-BoGgbKmO.js.map +0 -1
- package/dist/module-CTPfENDU.js.map +0 -1
- package/dist/module-CpP8D2Hl.d.ts.map +0 -1
- package/dist/module-D0SK3Nds.js.map +0 -1
- package/dist/processor-module-D0lRrrpW.js +0 -95
- package/dist/processor-module-D0lRrrpW.js.map +0 -1
- package/dist/subgraph-module-B3ipwOza.js +0 -92
- package/dist/subgraph-module-B3ipwOza.js.map +0 -1
- package/dist/switchboard-JOTFVXuj.js +0 -8
|
@@ -0,0 +1,793 @@
|
|
|
1
|
+
import { d as appModuleDocumentType, f as reducer, p as actions, r as utils } from "../utils-BRx88WW3.js";
|
|
2
|
+
import { t as createAppModuleDocument } from "../ph-factories-B6PSPkPQ.js";
|
|
3
|
+
import { d as documentEditorDocumentType, f as reducer$1, p as actions$1, r as utils$1 } from "../utils-CnZc-0E4.js";
|
|
4
|
+
import { t as createDocumentEditorDocument } from "../ph-factories-CJXfT_Z1.js";
|
|
5
|
+
import { d as processorModuleDocumentType, f as reducer$2, p as actions$2, r as utils$2 } from "../utils-Byjf33qt.js";
|
|
6
|
+
import { t as createProcessorModuleDocument } from "../ph-factories-B0_E6LY6.js";
|
|
7
|
+
import { d as subgraphModuleDocumentType, f as reducer$3, p as actions$3, r as utils$3 } from "../utils-CWO5kDus.js";
|
|
8
|
+
import { n as createSubgraphModuleDocument } from "../ph-factories-Bn_HTUfT.js";
|
|
9
|
+
import { documentModelDocumentModelModule } from "document-model";
|
|
10
|
+
import { PROCESSOR_APPS } from "@powerhousedao/shared/processors";
|
|
11
|
+
import { generateId as generateId$1 } from "@powerhousedao/shared/document-model";
|
|
12
|
+
import { getAppMetadata, getEditorMetadata, getOrCreateDirectory, getProcessorMetadata, getSubgraphMetadata, loadDocumentModelInDir } from "@powerhousedao/codegen/utils";
|
|
13
|
+
import { readdirSync } from "node:fs";
|
|
14
|
+
import { generateApp, generateDocumentModel, generateEditor, generateProcessor, generateSubgraph } from "@powerhousedao/codegen";
|
|
15
|
+
import { camelCase, kebabCase } from "change-case";
|
|
16
|
+
import { baseLoadFromFile, baseSaveToFile } from "document-model/node";
|
|
17
|
+
import { mkdir, readdir, rm, stat } from "node:fs/promises";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
//#region codegen/extract.ts
|
|
20
|
+
/**
|
|
21
|
+
* Inverse of the `generate*` functions in `@powerhousedao/codegen`.
|
|
22
|
+
*
|
|
23
|
+
* `generate*` reads source-of-truth documents and writes code into the project.
|
|
24
|
+
* `extract*` reads the project's existing code/files and produces the
|
|
25
|
+
* `powerhouse/*` documents that would have produced that code. Intended as a
|
|
26
|
+
* one-shot migration step toward the documents-as-source-of-truth model: run
|
|
27
|
+
* extraction, save the documents into `specs/`, and from then on regenerate
|
|
28
|
+
* from the specs.
|
|
29
|
+
*/
|
|
30
|
+
const isDefined = (v) => v != null;
|
|
31
|
+
function extractEditorDocuments(project) {
|
|
32
|
+
const { directory: editorsDir } = getOrCreateDirectory(project, "editors");
|
|
33
|
+
return editorsDir.getDirectories().map((dir) => getEditorMetadata(project, dir.getBaseName())).filter(isDefined).filter(({ documentTypes }) => !documentTypes.includes("powerhouse/document-drive")).map(({ name, documentTypes }) => {
|
|
34
|
+
const doc = createDocumentEditorDocument({ global: {
|
|
35
|
+
name,
|
|
36
|
+
documentTypes: documentTypes.map((documentType) => ({
|
|
37
|
+
id: generateId$1(),
|
|
38
|
+
documentType
|
|
39
|
+
})),
|
|
40
|
+
status: "CONFIRMED"
|
|
41
|
+
} });
|
|
42
|
+
doc.header.name = name;
|
|
43
|
+
return doc;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function extractAppDocuments(project) {
|
|
47
|
+
const { directory: editorsDir } = getOrCreateDirectory(project, "editors");
|
|
48
|
+
return editorsDir.getDirectories().map((dir) => getAppMetadata(project, dir.getBaseName())).filter(isDefined).map(({ name, allowedDocumentTypes, isDragAndDropEnabled }) => {
|
|
49
|
+
const doc = createAppModuleDocument({ global: {
|
|
50
|
+
name,
|
|
51
|
+
status: "CONFIRMED",
|
|
52
|
+
allowedDocumentTypes: allowedDocumentTypes.length > 0 ? allowedDocumentTypes : null,
|
|
53
|
+
isDragAndDropEnabled: isDragAndDropEnabled ?? true
|
|
54
|
+
} });
|
|
55
|
+
doc.header.name = name;
|
|
56
|
+
return doc;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function extractProcessorDocuments(project) {
|
|
60
|
+
const { directory: processorsDir } = getOrCreateDirectory(project, "processors");
|
|
61
|
+
return processorsDir.getDirectories().map((dir) => getProcessorMetadata(project, dir.getBaseName())).map(({ processorName, processorApps, processorType, documentTypes }) => {
|
|
62
|
+
const doc = createProcessorModuleDocument({ global: {
|
|
63
|
+
name: processorName,
|
|
64
|
+
type: processorType === "relationalDb" ? "relational" : processorType,
|
|
65
|
+
documentTypes: documentTypes.map((documentType) => ({
|
|
66
|
+
id: generateId$1(),
|
|
67
|
+
documentType
|
|
68
|
+
})),
|
|
69
|
+
status: "CONFIRMED",
|
|
70
|
+
processorApps: [...processorApps]
|
|
71
|
+
} });
|
|
72
|
+
doc.header.name = processorName;
|
|
73
|
+
return doc;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
function extractSubgraphDocuments(project) {
|
|
77
|
+
const { directory: subgraphsDir } = getOrCreateDirectory(project, "subgraphs");
|
|
78
|
+
return subgraphsDir.getDirectories().map((dir) => getSubgraphMetadata(project, dir.getBaseName()).subgraphName).filter(isDefined).map((name) => {
|
|
79
|
+
const doc = createSubgraphModuleDocument({ global: {
|
|
80
|
+
name,
|
|
81
|
+
status: "CONFIRMED"
|
|
82
|
+
} });
|
|
83
|
+
doc.header.name = name;
|
|
84
|
+
return doc;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function extractDocumentModelDocuments(project) {
|
|
88
|
+
const { directory: documentModelsDir } = getOrCreateDirectory(project, "document-models");
|
|
89
|
+
return readdirSync(documentModelsDir.getPath(), { withFileTypes: true }).map(loadDocumentModelInDir).filter(isDefined).map(buildDocumentModelDocument);
|
|
90
|
+
}
|
|
91
|
+
function buildDocumentModelDocument(globalState) {
|
|
92
|
+
const state = documentModelDocumentModelModule.utils.createState({ global: globalState });
|
|
93
|
+
const doc = documentModelDocumentModelModule.utils.createDocument(state);
|
|
94
|
+
doc.header.name = globalState.name;
|
|
95
|
+
return doc;
|
|
96
|
+
}
|
|
97
|
+
function extractAllDocuments(project) {
|
|
98
|
+
return {
|
|
99
|
+
documentModels: extractDocumentModelDocuments(project),
|
|
100
|
+
editors: extractEditorDocuments(project),
|
|
101
|
+
apps: extractAppDocuments(project),
|
|
102
|
+
processors: extractProcessorDocuments(project),
|
|
103
|
+
subgraphs: extractSubgraphDocuments(project)
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region codegen/generate-from-document.ts
|
|
108
|
+
/**
|
|
109
|
+
* Document-driven codegen wrappers. Each function takes a `PHDocument` of the
|
|
110
|
+
* matching vetra type and dispatches to the generic generate* in
|
|
111
|
+
* `@powerhousedao/codegen`. The state→args translation is lifted verbatim from
|
|
112
|
+
* `packages/vetra/processors/codegen/document-handlers/generators/*-generator.ts`
|
|
113
|
+
* so the agent-driven path and the reactor-driven path produce identical
|
|
114
|
+
* output.
|
|
115
|
+
*
|
|
116
|
+
* Callers are responsible for `await project.save()` after one or more
|
|
117
|
+
* `generate*FromDocument` calls — leaving it out here lets a CLI batch
|
|
118
|
+
* multiple specs into a single save.
|
|
119
|
+
*/
|
|
120
|
+
async function generateDocumentModelFromDocument(doc, project) {
|
|
121
|
+
await generateDocumentModel(doc.state.global, project);
|
|
122
|
+
}
|
|
123
|
+
async function generateEditorFromDocument(doc, project) {
|
|
124
|
+
const state = doc.state.global;
|
|
125
|
+
const documentTypes = state.documentTypes.map((dt) => dt.documentType);
|
|
126
|
+
await generateEditor({
|
|
127
|
+
editorName: state.name,
|
|
128
|
+
documentTypes,
|
|
129
|
+
editorId: kebabCase(state.name)
|
|
130
|
+
}, project);
|
|
131
|
+
}
|
|
132
|
+
async function generateProcessorFromDocument(doc, project) {
|
|
133
|
+
const state = doc.state.global;
|
|
134
|
+
let processorType;
|
|
135
|
+
if (state.type === "analytics") processorType = "analytics";
|
|
136
|
+
else if (state.type === "relational") processorType = "relationalDb";
|
|
137
|
+
else throw new Error(`Unsupported processor type: "${state.type}"`);
|
|
138
|
+
const documentTypes = state.documentTypes.map((dt) => dt.documentType);
|
|
139
|
+
const processorApps = state.processorApps;
|
|
140
|
+
if (!isProcessorApps(processorApps)) throw new Error(`Unsupported processor apps: ${processorApps.join(", ")}`);
|
|
141
|
+
await generateProcessor({
|
|
142
|
+
processorName: state.name,
|
|
143
|
+
processorType,
|
|
144
|
+
documentTypes,
|
|
145
|
+
processorApps
|
|
146
|
+
}, project);
|
|
147
|
+
}
|
|
148
|
+
async function generateSubgraphFromDocument(doc, project) {
|
|
149
|
+
await generateSubgraph(doc.state.global.name, project);
|
|
150
|
+
}
|
|
151
|
+
async function generateAppFromDocument(doc, project) {
|
|
152
|
+
const state = doc.state.global;
|
|
153
|
+
await generateApp({
|
|
154
|
+
appName: state.name,
|
|
155
|
+
appId: kebabCase(state.name),
|
|
156
|
+
allowedDocumentTypes: state.allowedDocumentTypes ?? [],
|
|
157
|
+
isDragAndDropEnabled: state.isDragAndDropEnabled
|
|
158
|
+
}, project);
|
|
159
|
+
}
|
|
160
|
+
function isProcessorApps(input) {
|
|
161
|
+
if (input.length === 0) return false;
|
|
162
|
+
if (new Set(input).size !== input.length) return false;
|
|
163
|
+
if (!input.every((i) => PROCESSOR_APPS.includes(i))) return false;
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region document-models/app-module/app-module.json
|
|
168
|
+
var app_module_default = {
|
|
169
|
+
id: "powerhouse/app",
|
|
170
|
+
name: "App Module",
|
|
171
|
+
author: {
|
|
172
|
+
"name": "Powerhouse",
|
|
173
|
+
"website": "https://powerhouse.inc"
|
|
174
|
+
},
|
|
175
|
+
extension: ".app",
|
|
176
|
+
description: "Declares a custom Drive App (a drive-level UI surface) shipped by a Vetra Reactor Package. Use one App Module document per drive app: configure which document types it handles, whether it accepts drag-and-drop, and mark it CONFIRMED to trigger codegen of the corresponding app scaffold under `apps/`.",
|
|
177
|
+
specifications: [{
|
|
178
|
+
"state": {
|
|
179
|
+
"local": {
|
|
180
|
+
"schema": "",
|
|
181
|
+
"examples": [],
|
|
182
|
+
"initialValue": ""
|
|
183
|
+
},
|
|
184
|
+
"global": {
|
|
185
|
+
"schema": "\"\"\"\nConfiguration for a Drive App contributed by the package. Drive apps render a\ncustom view at the drive level (instead of, or alongside, the document file\ntree) and can opt into which document types they accept.\n\"\"\"\ntype AppModuleState {\n \"\"\"Display name of the drive app. Also used as the source for the generated folder name under `apps/`.\"\"\"\n name: String!\n \"\"\"Lifecycle status. While DRAFT the app definition is editable and codegen is skipped; switching to CONFIRMED triggers app scaffold generation.\"\"\"\n status: StatusType!\n \"\"\"Document type ids this app handles. `null` means the app accepts any document type; an empty list means it accepts none.\"\"\"\n allowedDocumentTypes: [String!]\n \"\"\"Whether the app surface accepts dropped files from the user. Defaults to true.\"\"\"\n isDragAndDropEnabled: Boolean!\n}\n\n\"\"\"\nLifecycle status of a module definition.\n- DRAFT: still being edited; codegen does not run.\n- CONFIRMED: locked in; codegen produces the corresponding scaffold.\n\"\"\"\nenum StatusType {\n DRAFT\n CONFIRMED\n}",
|
|
186
|
+
"examples": [],
|
|
187
|
+
"initialValue": "{\n \"name\": \"\",\n \"status\": \"DRAFT\",\n \"allowedDocumentTypes\": null,\n \"isDragAndDropEnabled\": true\n}"
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
"modules": [{
|
|
191
|
+
"id": "d274599a-ceb5-4f2b-8651-b25787306734",
|
|
192
|
+
"name": "base_operations",
|
|
193
|
+
"description": "Set the app's identity, lifecycle status, and the document types it handles.",
|
|
194
|
+
"operations": [
|
|
195
|
+
{
|
|
196
|
+
"id": "f2ba2ddc-8527-4162-93bd-e045e9932013",
|
|
197
|
+
"name": "SET_APP_NAME",
|
|
198
|
+
"description": "Set the display name of the drive app. Also determines the generated folder name under `apps/`.",
|
|
199
|
+
"schema": "input SetAppNameInput {\n name: String!\n}",
|
|
200
|
+
"template": "",
|
|
201
|
+
"reducer": "",
|
|
202
|
+
"errors": [],
|
|
203
|
+
"examples": [],
|
|
204
|
+
"scope": "global"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"id": "c842efa4-154c-4fd9-9d12-42bec51af4e9",
|
|
208
|
+
"name": "SET_APP_STATUS",
|
|
209
|
+
"description": "Move the app between DRAFT and CONFIRMED. Codegen only runs once the status is CONFIRMED.",
|
|
210
|
+
"schema": "input SetAppStatusInput {\n status: StatusType!\n}",
|
|
211
|
+
"template": "",
|
|
212
|
+
"reducer": "",
|
|
213
|
+
"errors": [],
|
|
214
|
+
"examples": [],
|
|
215
|
+
"scope": "global"
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"id": "7376f168-695f-4aef-94d0-6e381666358c",
|
|
219
|
+
"name": "ADD_DOCUMENT_TYPE",
|
|
220
|
+
"description": "Append a document type id to the list of types this app handles. Initializes the list if it was `null` (accept-any).",
|
|
221
|
+
"schema": "input AddDocumentTypeInput {\n documentType: String!\n}",
|
|
222
|
+
"template": "",
|
|
223
|
+
"reducer": "",
|
|
224
|
+
"errors": [],
|
|
225
|
+
"examples": [],
|
|
226
|
+
"scope": "global"
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"id": "310e4e5b-3f14-4e9a-8e09-5583c7698a65",
|
|
230
|
+
"name": "REMOVE_DOCUMENT_TYPE",
|
|
231
|
+
"description": "Remove a document type id from the handled list. No-op if the type is not present.",
|
|
232
|
+
"schema": "input RemoveDocumentTypeInput {\n documentType: String!\n}",
|
|
233
|
+
"template": "",
|
|
234
|
+
"reducer": "",
|
|
235
|
+
"errors": [],
|
|
236
|
+
"examples": [],
|
|
237
|
+
"scope": "global"
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"id": "b365727a-7df3-48f0-a4f8-02362f02ad1d",
|
|
241
|
+
"name": "SET_DOCUMENT_TYPES",
|
|
242
|
+
"description": "Replace the entire allowed-document-types list in one call. Pass an empty list to accept none.",
|
|
243
|
+
"schema": "input SetDocumentTypesInput {\n documentTypes: [String!]!\n}",
|
|
244
|
+
"template": "",
|
|
245
|
+
"reducer": "",
|
|
246
|
+
"errors": [],
|
|
247
|
+
"examples": [],
|
|
248
|
+
"scope": "global"
|
|
249
|
+
}
|
|
250
|
+
]
|
|
251
|
+
}, {
|
|
252
|
+
"id": "270faa10-92e9-40d0-b128-2de32704bcb5",
|
|
253
|
+
"name": "dnd_operations",
|
|
254
|
+
"description": "Toggle drag-and-drop file handling for the app surface.",
|
|
255
|
+
"operations": [{
|
|
256
|
+
"id": "077b1ab8-cb32-4b4e-a1fa-76178188c6a1",
|
|
257
|
+
"name": "SET_DRAG_AND_DROP_ENABLED",
|
|
258
|
+
"description": "Enable or disable drag-and-drop file handling on the app surface.",
|
|
259
|
+
"schema": "input SetDragAndDropEnabledInput {\n enabled: Boolean!\n}",
|
|
260
|
+
"template": "",
|
|
261
|
+
"reducer": "",
|
|
262
|
+
"errors": [],
|
|
263
|
+
"examples": [],
|
|
264
|
+
"scope": "global"
|
|
265
|
+
}]
|
|
266
|
+
}],
|
|
267
|
+
"version": 1,
|
|
268
|
+
"changeLog": []
|
|
269
|
+
}]
|
|
270
|
+
};
|
|
271
|
+
//#endregion
|
|
272
|
+
//#region document-models/document-editor/document-editor.json
|
|
273
|
+
var document_editor_default = {
|
|
274
|
+
id: "powerhouse/document-editor",
|
|
275
|
+
name: "Document Editor",
|
|
276
|
+
author: {
|
|
277
|
+
"name": "Powerhouse",
|
|
278
|
+
"website": "https://powerhouse.inc"
|
|
279
|
+
},
|
|
280
|
+
extension: ".editor",
|
|
281
|
+
description: "Declares a document editor (a React UI for editing instances of one or more document models) shipped by a Vetra Reactor Package. Create one Document Editor document per editor, list the document types it can edit, then mark it CONFIRMED to trigger codegen of the editor scaffold under `editors/`. The boilerplate it produces is what you then customize.",
|
|
282
|
+
specifications: [{
|
|
283
|
+
"state": {
|
|
284
|
+
"local": {
|
|
285
|
+
"schema": "",
|
|
286
|
+
"examples": [],
|
|
287
|
+
"initialValue": ""
|
|
288
|
+
},
|
|
289
|
+
"global": {
|
|
290
|
+
"schema": "\"\"\"\nConfiguration for a document editor contributed by the package. The editor is\nregistered against every document type listed in `documentTypes`; Connect picks\nthe first matching editor when opening a document.\n\"\"\"\ntype DocumentEditorState {\n \"\"\"Display name of the editor. Also determines the generated folder name under `editors/`.\"\"\"\n name: String!\n \"\"\"Document types this editor can edit. Each entry has a stable id so it can be removed individually.\"\"\"\n documentTypes: [DocumentTypeItem!]!\n \"\"\"Lifecycle status. While DRAFT the editor definition is editable and codegen is skipped; switching to CONFIRMED triggers scaffold generation.\"\"\"\n status: StatusType!\n}\n\n\"\"\"A document type id (e.g. 'powerhouse/document-drive') attached to the editor with a stable entry id.\"\"\"\ntype DocumentTypeItem {\n \"\"\"Stable identifier for the entry; used to remove it.\"\"\"\n id: OID!\n \"\"\"Document type id this editor handles (e.g. 'my-org/invoice').\"\"\"\n documentType: String!\n}\n\n\"\"\"\nLifecycle status of a module definition.\n- DRAFT: still being edited; codegen does not run.\n- CONFIRMED: locked in; codegen produces the corresponding scaffold.\n\"\"\"\nenum StatusType {\n DRAFT\n CONFIRMED\n}",
|
|
291
|
+
"examples": [],
|
|
292
|
+
"initialValue": "{\n \"name\": \"\",\n \"documentTypes\": [],\n \"status\": \"DRAFT\"\n}"
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
"modules": [{
|
|
296
|
+
"id": "73f6d103-47cb-4074-a736-a3eaf5d079bf",
|
|
297
|
+
"name": "base_operations",
|
|
298
|
+
"description": "Set the editor's identity, lifecycle status, and the document types it handles.",
|
|
299
|
+
"operations": [
|
|
300
|
+
{
|
|
301
|
+
"id": "50a16f00-d25a-4c97-9632-4ce3b951a402",
|
|
302
|
+
"name": "SET_EDITOR_NAME",
|
|
303
|
+
"description": "Set the display name of the editor. Also determines the generated folder name under `editors/`.",
|
|
304
|
+
"schema": "input SetEditorNameInput {\n name: String!\n}",
|
|
305
|
+
"template": "",
|
|
306
|
+
"reducer": "",
|
|
307
|
+
"errors": [],
|
|
308
|
+
"examples": [],
|
|
309
|
+
"scope": "global"
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
"id": "acee4272-f29e-4a19-aaf9-bae2cb6652ab",
|
|
313
|
+
"name": "ADD_DOCUMENT_TYPE",
|
|
314
|
+
"description": "Register a document type this editor can edit. Caller supplies a stable id so the entry can be removed later.",
|
|
315
|
+
"schema": "input AddDocumentTypeInput {\n id: OID!\n documentType: String!\n}",
|
|
316
|
+
"template": "",
|
|
317
|
+
"reducer": "",
|
|
318
|
+
"errors": [],
|
|
319
|
+
"examples": [],
|
|
320
|
+
"scope": "global"
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
"id": "6c549776-0fc3-4632-a6c7-8721fa6ee41c",
|
|
324
|
+
"name": "REMOVE_DOCUMENT_TYPE",
|
|
325
|
+
"description": "Remove a registered document type entry by its id.",
|
|
326
|
+
"schema": "input RemoveDocumentTypeInput {\n id: OID!\n}",
|
|
327
|
+
"template": "",
|
|
328
|
+
"reducer": "",
|
|
329
|
+
"errors": [],
|
|
330
|
+
"examples": [],
|
|
331
|
+
"scope": "global"
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
"id": "e9aa7f08-553b-452f-a494-126ace6b15f7",
|
|
335
|
+
"name": "SET_EDITOR_STATUS",
|
|
336
|
+
"description": "Move the editor between DRAFT and CONFIRMED. Codegen only runs once the status is CONFIRMED — leaving it DRAFT means no editor files are produced.",
|
|
337
|
+
"schema": "input SetEditorStatusInput {\n status: StatusType!\n}",
|
|
338
|
+
"template": "",
|
|
339
|
+
"reducer": "",
|
|
340
|
+
"errors": [],
|
|
341
|
+
"examples": [],
|
|
342
|
+
"scope": "global"
|
|
343
|
+
}
|
|
344
|
+
]
|
|
345
|
+
}],
|
|
346
|
+
"version": 1,
|
|
347
|
+
"changeLog": []
|
|
348
|
+
}]
|
|
349
|
+
};
|
|
350
|
+
//#endregion
|
|
351
|
+
//#region document-models/processor-module/processor-module.json
|
|
352
|
+
var processor_module_default = {
|
|
353
|
+
id: "powerhouse/processor",
|
|
354
|
+
name: "Processor Module",
|
|
355
|
+
author: {
|
|
356
|
+
"name": "Powerhouse",
|
|
357
|
+
"website": "https://powerhouse.inc"
|
|
358
|
+
},
|
|
359
|
+
extension: ".processor",
|
|
360
|
+
description: "Declares a processor (a server-side function that reacts to document operations to build read models, send notifications, sync to external systems, etc.) shipped by a Vetra Reactor Package. Create one Processor Module document per processor: pick the processor type, list the document types it subscribes to, attach it to any drive apps that should run it, and mark it CONFIRMED to trigger codegen of the processor scaffold under `processors/`.",
|
|
361
|
+
specifications: [{
|
|
362
|
+
"state": {
|
|
363
|
+
"local": {
|
|
364
|
+
"schema": "",
|
|
365
|
+
"examples": [],
|
|
366
|
+
"initialValue": ""
|
|
367
|
+
},
|
|
368
|
+
"global": {
|
|
369
|
+
"schema": "\"\"\"\nConfiguration for a processor contributed by the package. A processor receives\noperations from documents of matching types and runs server-side logic (e.g.\nbuilding a read model, indexing, syncing to an external system).\n\"\"\"\ntype ProcessorModuleState {\n \"\"\"Display name of the processor. Also determines the generated folder name under `processors/`.\"\"\"\n name: String!\n \"\"\"Processor implementation kind (e.g. 'read-model', 'relational-db'). Determines which scaffold codegen emits.\"\"\"\n type: String!\n \"\"\"Document types this processor subscribes to. Each entry has a stable id so it can be removed individually.\"\"\"\n documentTypes: [DocumentTypeItem!]!\n \"\"\"Lifecycle status. While DRAFT the processor definition is editable and codegen is skipped; switching to CONFIRMED triggers scaffold generation.\"\"\"\n status: StatusType!\n \"\"\"Drive-app names this processor is attached to. The processor runs in the context of each listed app.\"\"\"\n processorApps: [String!]!\n}\n\n\"\"\"A document type id (e.g. 'my-org/invoice') attached to the processor with a stable entry id.\"\"\"\ntype DocumentTypeItem {\n \"\"\"Stable identifier for the entry; used to remove it.\"\"\"\n id: OID!\n \"\"\"Document type id this processor subscribes to.\"\"\"\n documentType: String!\n}\n\n\"\"\"\nLifecycle status of a module definition.\n- DRAFT: still being edited; codegen does not run.\n- CONFIRMED: locked in; codegen produces the corresponding scaffold.\n\"\"\"\nenum StatusType {\n DRAFT\n CONFIRMED\n}",
|
|
370
|
+
"examples": [],
|
|
371
|
+
"initialValue": "{\n \"name\": \"\",\n \"type\": \"\",\n \"documentTypes\": [],\n \"status\": \"DRAFT\",\n \"processorApps\": []\n}"
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
"modules": [{
|
|
375
|
+
"id": "91ad39c1-4e8b-4127-b3c8-e835b85e6360",
|
|
376
|
+
"name": "base_operations",
|
|
377
|
+
"description": "Set the processor's identity, kind, lifecycle status, subscribed document types, and attached drive apps.",
|
|
378
|
+
"operations": [
|
|
379
|
+
{
|
|
380
|
+
"id": "6f3a5c90-39f2-4302-a073-6195a71c5054",
|
|
381
|
+
"name": "SET_PROCESSOR_NAME",
|
|
382
|
+
"description": "Set the display name of the processor. Also determines the generated folder name under `processors/`.",
|
|
383
|
+
"schema": "input SetProcessorNameInput {\n name: String!\n}",
|
|
384
|
+
"template": "",
|
|
385
|
+
"reducer": "",
|
|
386
|
+
"errors": [],
|
|
387
|
+
"examples": [],
|
|
388
|
+
"scope": "global"
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
"id": "b8f28bb4-c6ae-40e6-86fa-29ef14ff8667",
|
|
392
|
+
"name": "SET_PROCESSOR_TYPE",
|
|
393
|
+
"description": "Set the processor implementation kind (e.g. 'read-model', 'relational-db'). Determines which scaffold codegen emits.",
|
|
394
|
+
"schema": "input SetProcessorTypeInput {\n type: String!\n}",
|
|
395
|
+
"template": "",
|
|
396
|
+
"reducer": "",
|
|
397
|
+
"errors": [],
|
|
398
|
+
"examples": [],
|
|
399
|
+
"scope": "global"
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
"id": "fbbd7a71-c495-4efc-b8f6-1e57798dbbb4",
|
|
403
|
+
"name": "ADD_DOCUMENT_TYPE",
|
|
404
|
+
"description": "Subscribe the processor to a document type. Caller supplies a stable id so the entry can be removed later.",
|
|
405
|
+
"schema": "input AddDocumentTypeInput {\n id: OID!\n documentType: String!\n}",
|
|
406
|
+
"template": "",
|
|
407
|
+
"reducer": "",
|
|
408
|
+
"errors": [],
|
|
409
|
+
"examples": [],
|
|
410
|
+
"scope": "global"
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
"id": "544d413f-423c-4d97-9570-84a19bffeab9",
|
|
414
|
+
"name": "REMOVE_DOCUMENT_TYPE",
|
|
415
|
+
"description": "Unsubscribe the processor from a document type by removing its entry id.",
|
|
416
|
+
"schema": "input RemoveDocumentTypeInput {\n id: OID!\n}",
|
|
417
|
+
"template": "",
|
|
418
|
+
"reducer": "",
|
|
419
|
+
"errors": [],
|
|
420
|
+
"examples": [],
|
|
421
|
+
"scope": "global"
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
"id": "df5eb500-7308-498c-9b80-028878ee198b",
|
|
425
|
+
"name": "ADD_PROCESSOR_APP",
|
|
426
|
+
"description": "Attach the processor to a drive app by name. The processor will run in the context of that app.",
|
|
427
|
+
"schema": "input AddProcessorAppInput {\n processorApp: String!\n}",
|
|
428
|
+
"template": "",
|
|
429
|
+
"reducer": "",
|
|
430
|
+
"errors": [],
|
|
431
|
+
"examples": [],
|
|
432
|
+
"scope": "global"
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
"id": "07e4168f-1a7b-41ef-953d-219028be7bb9",
|
|
436
|
+
"name": "REMOVE_PROCESSOR_APP",
|
|
437
|
+
"description": "Detach the processor from a drive app by name. No-op if the app is not currently attached.",
|
|
438
|
+
"schema": "input RemoveProcessorAppInput {\n processorApp: String!\n}",
|
|
439
|
+
"template": "",
|
|
440
|
+
"reducer": "",
|
|
441
|
+
"errors": [],
|
|
442
|
+
"examples": [],
|
|
443
|
+
"scope": "global"
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
"id": "7b6706eb-5e25-4d64-829a-e3a251380fd1",
|
|
447
|
+
"name": "SET_PROCESSOR_STATUS",
|
|
448
|
+
"description": "Move the processor between DRAFT and CONFIRMED. Codegen only runs once the status is CONFIRMED.",
|
|
449
|
+
"schema": "input SetProcessorStatusInput {\n status: StatusType!\n}",
|
|
450
|
+
"template": "",
|
|
451
|
+
"reducer": "",
|
|
452
|
+
"errors": [],
|
|
453
|
+
"examples": [],
|
|
454
|
+
"scope": "global"
|
|
455
|
+
}
|
|
456
|
+
]
|
|
457
|
+
}],
|
|
458
|
+
"version": 1,
|
|
459
|
+
"changeLog": []
|
|
460
|
+
}]
|
|
461
|
+
};
|
|
462
|
+
//#endregion
|
|
463
|
+
//#region document-models/subgraph-module/subgraph-module.json
|
|
464
|
+
var subgraph_module_default = {
|
|
465
|
+
id: "powerhouse/subgraph",
|
|
466
|
+
name: "Subgraph Module",
|
|
467
|
+
author: {
|
|
468
|
+
"name": "Powerhouse",
|
|
469
|
+
"website": "https://powerhouse.inc"
|
|
470
|
+
},
|
|
471
|
+
extension: ".subgraph",
|
|
472
|
+
description: "Declares a GraphQL subgraph (a slice of the Switchboard API contributed by the package) shipped by a Vetra Reactor Package. Create one Subgraph Module document per subgraph, then mark it CONFIRMED to trigger codegen of the subgraph scaffold under `subgraphs/` — the resolvers and schema you flesh out there are stitched into the Switchboard graph at runtime.",
|
|
473
|
+
specifications: [{
|
|
474
|
+
"state": {
|
|
475
|
+
"local": {
|
|
476
|
+
"schema": "",
|
|
477
|
+
"examples": [],
|
|
478
|
+
"initialValue": ""
|
|
479
|
+
},
|
|
480
|
+
"global": {
|
|
481
|
+
"schema": "\"\"\"\nConfiguration for a GraphQL subgraph contributed by the package. The subgraph\nis served by Switchboard and stitched into the unified Powerhouse API.\n\"\"\"\ntype SubgraphModuleState {\n \"\"\"Display name of the subgraph. Also determines the generated folder name under `subgraphs/` and the route segment Switchboard mounts it at.\"\"\"\n name: String!\n \"\"\"Lifecycle status. While DRAFT the subgraph definition is editable and codegen is skipped; switching to CONFIRMED triggers scaffold generation.\"\"\"\n status: StatusType!\n}\n\n\"\"\"\nLifecycle status of a module definition.\n- DRAFT: still being edited; codegen does not run.\n- CONFIRMED: locked in; codegen produces the corresponding scaffold.\n\"\"\"\nenum StatusType {\n DRAFT\n CONFIRMED\n}",
|
|
482
|
+
"examples": [],
|
|
483
|
+
"initialValue": "{\n \"name\": \"\",\n \"status\": \"DRAFT\"\n}"
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
"modules": [{
|
|
487
|
+
"id": "8af5bda9-6fc7-4427-bfed-1d32d76a552f",
|
|
488
|
+
"name": "base_operations",
|
|
489
|
+
"description": "Set the subgraph's identity and lifecycle status.",
|
|
490
|
+
"operations": [{
|
|
491
|
+
"id": "d7cd6b6b-01ea-42c8-97e2-288e04b50b42",
|
|
492
|
+
"name": "SET_SUBGRAPH_NAME",
|
|
493
|
+
"description": "Set the display name of the subgraph. Also determines the generated folder under `subgraphs/` and the route segment Switchboard mounts it at.",
|
|
494
|
+
"schema": "input SetSubgraphNameInput {\n name: String!\n}",
|
|
495
|
+
"template": "",
|
|
496
|
+
"reducer": "",
|
|
497
|
+
"errors": [],
|
|
498
|
+
"examples": [],
|
|
499
|
+
"scope": "global"
|
|
500
|
+
}, {
|
|
501
|
+
"id": "5a20e641-dc36-428e-8924-ecb07f3f1b94",
|
|
502
|
+
"name": "SET_SUBGRAPH_STATUS",
|
|
503
|
+
"description": "Move the subgraph between DRAFT and CONFIRMED. Codegen only runs once the status is CONFIRMED.",
|
|
504
|
+
"schema": "input SetSubgraphStatusInput {\n status: StatusType!\n}",
|
|
505
|
+
"template": "",
|
|
506
|
+
"reducer": "",
|
|
507
|
+
"errors": [],
|
|
508
|
+
"examples": [],
|
|
509
|
+
"scope": "global"
|
|
510
|
+
}]
|
|
511
|
+
}],
|
|
512
|
+
"version": 1,
|
|
513
|
+
"changeLog": []
|
|
514
|
+
}]
|
|
515
|
+
};
|
|
516
|
+
//#endregion
|
|
517
|
+
//#region codegen/specs.ts
|
|
518
|
+
const SPECS_DIRNAME = "specs";
|
|
519
|
+
const documentModelDocumentType = "powerhouse/document-model";
|
|
520
|
+
const SPECS = {
|
|
521
|
+
[documentModelDocumentType]: {
|
|
522
|
+
documentType: documentModelDocumentType,
|
|
523
|
+
subdir: "document-models",
|
|
524
|
+
reducer: documentModelDocumentModelModule.reducer,
|
|
525
|
+
utils: documentModelDocumentModelModule.utils,
|
|
526
|
+
actions: documentModelDocumentModelModule.actions,
|
|
527
|
+
createDocument: documentModelDocumentModelModule.utils.createDocument,
|
|
528
|
+
jsonSpec: documentModelDocumentModelModule.documentModel.global
|
|
529
|
+
},
|
|
530
|
+
[documentEditorDocumentType]: {
|
|
531
|
+
documentType: documentEditorDocumentType,
|
|
532
|
+
subdir: "editors",
|
|
533
|
+
reducer: reducer$1,
|
|
534
|
+
utils: utils$1,
|
|
535
|
+
actions: actions$1,
|
|
536
|
+
createDocument: createDocumentEditorDocument,
|
|
537
|
+
jsonSpec: document_editor_default
|
|
538
|
+
},
|
|
539
|
+
[processorModuleDocumentType]: {
|
|
540
|
+
documentType: processorModuleDocumentType,
|
|
541
|
+
subdir: "processors",
|
|
542
|
+
reducer: reducer$2,
|
|
543
|
+
utils: utils$2,
|
|
544
|
+
actions: actions$2,
|
|
545
|
+
createDocument: createProcessorModuleDocument,
|
|
546
|
+
jsonSpec: processor_module_default
|
|
547
|
+
},
|
|
548
|
+
[subgraphModuleDocumentType]: {
|
|
549
|
+
documentType: subgraphModuleDocumentType,
|
|
550
|
+
subdir: "subgraphs",
|
|
551
|
+
reducer: reducer$3,
|
|
552
|
+
utils: utils$3,
|
|
553
|
+
actions: actions$3,
|
|
554
|
+
createDocument: createSubgraphModuleDocument,
|
|
555
|
+
jsonSpec: subgraph_module_default
|
|
556
|
+
},
|
|
557
|
+
[appModuleDocumentType]: {
|
|
558
|
+
documentType: appModuleDocumentType,
|
|
559
|
+
subdir: "apps",
|
|
560
|
+
reducer,
|
|
561
|
+
utils,
|
|
562
|
+
actions,
|
|
563
|
+
createDocument: createAppModuleDocument,
|
|
564
|
+
jsonSpec: app_module_default
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
function getSpecEntry(documentType) {
|
|
568
|
+
const entry = SPECS[documentType];
|
|
569
|
+
if (!entry) throw new Error(`No spec handler registered for "${documentType}"`);
|
|
570
|
+
return entry;
|
|
571
|
+
}
|
|
572
|
+
function listSpecDocumentTypes() {
|
|
573
|
+
return Object.keys(SPECS);
|
|
574
|
+
}
|
|
575
|
+
/** Returns the directory that holds specs for a given document type. */
|
|
576
|
+
function specDir(projectDir, documentType) {
|
|
577
|
+
return join(projectDir, SPECS_DIRNAME, getSpecEntry(documentType).subdir);
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Deterministic path for a doc-type + display name.
|
|
581
|
+
* Format: `<projectDir>/<specsDir>/<subdir>/<kebab-name>.<ext>.phd`.
|
|
582
|
+
*/
|
|
583
|
+
function specPath(projectDir, documentType, name) {
|
|
584
|
+
const entry = getSpecEntry(documentType);
|
|
585
|
+
const baseName = kebabCase(name);
|
|
586
|
+
const ext = stripLeadingDot(entry.utils.fileExtension);
|
|
587
|
+
return join(specDir(projectDir, documentType), `${baseName}.${ext}.phd`);
|
|
588
|
+
}
|
|
589
|
+
function stripLeadingDot(ext) {
|
|
590
|
+
return ext.startsWith(".") ? ext.slice(1) : ext;
|
|
591
|
+
}
|
|
592
|
+
/** Saves a document into `<projectDir>/specs/<subdir>/<kebab-name>.<ext>.phd`. */
|
|
593
|
+
async function saveSpec(doc, projectDir) {
|
|
594
|
+
const entry = getSpecEntry(doc.header.documentType);
|
|
595
|
+
const dir = specDir(projectDir, doc.header.documentType);
|
|
596
|
+
await mkdir(dir, { recursive: true });
|
|
597
|
+
const name = kebabCase(doc.header.name || "untitled");
|
|
598
|
+
return baseSaveToFile(doc, dir, stripLeadingDot(entry.utils.fileExtension), name);
|
|
599
|
+
}
|
|
600
|
+
//#endregion
|
|
601
|
+
//#region codegen/spec-api.ts
|
|
602
|
+
/** Mirrors reactor-mcp `getDocumentModels`. */
|
|
603
|
+
function getDocumentModels() {
|
|
604
|
+
return listSpecDocumentTypes().map((documentType) => {
|
|
605
|
+
const { jsonSpec } = getSpecEntry(documentType);
|
|
606
|
+
return {
|
|
607
|
+
name: jsonSpec.name,
|
|
608
|
+
type: jsonSpec.id,
|
|
609
|
+
description: jsonSpec.description ?? "",
|
|
610
|
+
extension: jsonSpec.extension ?? "",
|
|
611
|
+
authorName: jsonSpec.author?.name ?? "",
|
|
612
|
+
authorWebsite: jsonSpec.author?.website ?? ""
|
|
613
|
+
};
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
/** Mirrors reactor-mcp `getDocumentModelSchema`. Returns the full
|
|
617
|
+
* `DocumentModelGlobalState` (schema + operations + errors), same payload
|
|
618
|
+
* shape reactor-mcp serves. */
|
|
619
|
+
function getDocumentModelSchema(documentType) {
|
|
620
|
+
return getSpecEntry(documentType).jsonSpec;
|
|
621
|
+
}
|
|
622
|
+
/** Mirrors reactor-mcp `getDocument`. Path = file location on disk. */
|
|
623
|
+
async function getDocument(path) {
|
|
624
|
+
const subdir = pathSubdir(path);
|
|
625
|
+
const hint = subdir ? listSpecDocumentTypes().find((t) => getSpecEntry(t).subdir === subdir) : void 0;
|
|
626
|
+
const order = hint ? [hint, ...listSpecDocumentTypes().filter((t) => t !== hint)] : listSpecDocumentTypes();
|
|
627
|
+
let lastError;
|
|
628
|
+
for (const documentType of order) {
|
|
629
|
+
const { reducer } = getSpecEntry(documentType);
|
|
630
|
+
try {
|
|
631
|
+
return await baseLoadFromFile(path, reducer);
|
|
632
|
+
} catch (err) {
|
|
633
|
+
lastError = err;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
throw new Error(`Failed to load spec at "${path}": no registered reducer accepted it. Last error: ${String(lastError)}`);
|
|
637
|
+
}
|
|
638
|
+
/** Mirrors reactor-mcp `getDocuments`. `parentId` is the project directory
|
|
639
|
+
* containing `specs/`. */
|
|
640
|
+
async function getDocuments(projectDir, opts = {}) {
|
|
641
|
+
const documentTypes = opts.documentType ? [opts.documentType] : listSpecDocumentTypes();
|
|
642
|
+
const results = [];
|
|
643
|
+
for (const documentType of documentTypes) {
|
|
644
|
+
const dir = specDir(projectDir, documentType);
|
|
645
|
+
if (!await stat(dir).then((s) => s.isDirectory(), () => false)) continue;
|
|
646
|
+
const entries = await readdir(dir);
|
|
647
|
+
for (const entry of entries) {
|
|
648
|
+
if (!entry.endsWith(".phd")) continue;
|
|
649
|
+
results.push(await getDocument(join(dir, entry)));
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return results;
|
|
653
|
+
}
|
|
654
|
+
/** Mirrors reactor-mcp `createDocument`. Returns an in-memory document; the
|
|
655
|
+
* caller persists with `saveSpec`. */
|
|
656
|
+
function createDocument(documentType, opts = {}) {
|
|
657
|
+
const doc = getSpecEntry(documentType).createDocument(opts.initialState);
|
|
658
|
+
if (opts.name) doc.header.name = opts.name;
|
|
659
|
+
return doc;
|
|
660
|
+
}
|
|
661
|
+
/** Mirrors reactor-mcp `addActions`. Validates every action up front against
|
|
662
|
+
* the document model's latest specification, then applies them in order via
|
|
663
|
+
* the reducer. If any action is invalid, throws an aggregated error and
|
|
664
|
+
* leaves `doc` untouched. */
|
|
665
|
+
function addActions(doc, actions) {
|
|
666
|
+
const entry = getSpecEntry(doc.header.documentType);
|
|
667
|
+
const built = validateAndBuildActions(entry, actions);
|
|
668
|
+
let current = doc;
|
|
669
|
+
for (let i = 0; i < built.length; i++) {
|
|
670
|
+
const action = built[i];
|
|
671
|
+
current = entry.reducer(current, action);
|
|
672
|
+
const scope = action.scope ?? "global";
|
|
673
|
+
const ops = current.operations[scope];
|
|
674
|
+
const last = ops?.[ops.length - 1];
|
|
675
|
+
if (last?.error) throw formatValidationError([{
|
|
676
|
+
index: i,
|
|
677
|
+
type: actions[i].type ?? "<missing>",
|
|
678
|
+
errors: [`Reducer error: ${last.error}`]
|
|
679
|
+
}]);
|
|
680
|
+
}
|
|
681
|
+
return current;
|
|
682
|
+
}
|
|
683
|
+
/** Validate a list of actions without applying them. Returns one entry per
|
|
684
|
+
* invalid action with all of its problems aggregated. Used by `addActions`
|
|
685
|
+
* and exposed for callers that want a dry-run check. */
|
|
686
|
+
function validateActions(documentType, actions) {
|
|
687
|
+
const entry = getSpecEntry(documentType);
|
|
688
|
+
const failures = [];
|
|
689
|
+
for (let i = 0; i < actions.length; i++) {
|
|
690
|
+
const errors = collectActionErrors(entry, actions[i]);
|
|
691
|
+
if (errors.length > 0) failures.push({
|
|
692
|
+
index: i,
|
|
693
|
+
type: actions[i].type ?? "<missing>",
|
|
694
|
+
errors
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
return failures;
|
|
698
|
+
}
|
|
699
|
+
/** Run validateActions across the batch and, on success, materialize the
|
|
700
|
+
* built Action objects. On any failure throws with every action's errors
|
|
701
|
+
* aggregated — the caller never sees a partial apply. */
|
|
702
|
+
function validateAndBuildActions(entry, actions) {
|
|
703
|
+
const built = [];
|
|
704
|
+
const failures = [];
|
|
705
|
+
for (let i = 0; i < actions.length; i++) {
|
|
706
|
+
const item = actions[i];
|
|
707
|
+
const errors = collectActionErrors(entry, item);
|
|
708
|
+
if (errors.length > 0) {
|
|
709
|
+
failures.push({
|
|
710
|
+
index: i,
|
|
711
|
+
type: item.type ?? "<missing>",
|
|
712
|
+
errors
|
|
713
|
+
});
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
built.push(buildAction(entry.actions, item));
|
|
717
|
+
}
|
|
718
|
+
if (failures.length > 0) throw formatValidationError(failures);
|
|
719
|
+
return built;
|
|
720
|
+
}
|
|
721
|
+
function collectActionErrors(entry, item) {
|
|
722
|
+
const errors = [];
|
|
723
|
+
if (typeof item.type !== "string" || item.type.length === 0) {
|
|
724
|
+
errors.push("Missing `type` field");
|
|
725
|
+
return errors;
|
|
726
|
+
}
|
|
727
|
+
const specs = entry.jsonSpec.specifications;
|
|
728
|
+
if (!specs || specs.length === 0) {
|
|
729
|
+
errors.push("Document model has no specifications");
|
|
730
|
+
return errors;
|
|
731
|
+
}
|
|
732
|
+
const latest = specs[specs.length - 1];
|
|
733
|
+
let operation;
|
|
734
|
+
for (const module of latest.modules ?? []) {
|
|
735
|
+
const found = (module.operations ?? []).find((op) => op.name === item.type);
|
|
736
|
+
if (found) {
|
|
737
|
+
operation = found;
|
|
738
|
+
break;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
if (!operation) errors.push(`Operation "${item.type}" is not defined in any module of the document model`);
|
|
742
|
+
const creatorName = camelCase(item.type);
|
|
743
|
+
const creator = entry.actions[creatorName];
|
|
744
|
+
if (!creator) errors.push(`No action creator found.`);
|
|
745
|
+
else try {
|
|
746
|
+
creator(item.input);
|
|
747
|
+
} catch (err) {
|
|
748
|
+
errors.push(`Input validation error: ${err instanceof Error ? err.message : String(err)}`);
|
|
749
|
+
}
|
|
750
|
+
if (operation?.scope && item.scope && item.scope !== operation.scope) errors.push(`Scope "${item.scope}" does not match operation scope "${operation.scope}"`);
|
|
751
|
+
return errors;
|
|
752
|
+
}
|
|
753
|
+
function formatValidationError(failures) {
|
|
754
|
+
const lines = failures.map((f) => ` [${f.index}] ${f.type}: ${f.errors.join("; ")}`);
|
|
755
|
+
const header = failures.length === 1 ? "1 action failed validation:" : `${failures.length} actions failed validation:`;
|
|
756
|
+
const err = new Error([header, ...lines].join("\n"));
|
|
757
|
+
err.failures = failures;
|
|
758
|
+
return err;
|
|
759
|
+
}
|
|
760
|
+
function buildAction(actionsModule, item) {
|
|
761
|
+
const creatorName = camelCase(item.type);
|
|
762
|
+
const creator = actionsModule[creatorName];
|
|
763
|
+
if (!creator) throw new Error(`No action creator found for "${item.type}" (looked up as "${creatorName}")`);
|
|
764
|
+
const action = creator(item.input);
|
|
765
|
+
if (item.scope) action.scope = item.scope;
|
|
766
|
+
return action;
|
|
767
|
+
}
|
|
768
|
+
/** Mirrors reactor-mcp `deleteDocument`. Removes the spec file from disk. */
|
|
769
|
+
async function deleteDocument(path) {
|
|
770
|
+
try {
|
|
771
|
+
await rm(path);
|
|
772
|
+
return { success: true };
|
|
773
|
+
} catch {
|
|
774
|
+
return { success: false };
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
async function addActionsAndSave(path, projectDir, actions) {
|
|
778
|
+
const next = addActions(await getDocument(path), actions);
|
|
779
|
+
return {
|
|
780
|
+
doc: next,
|
|
781
|
+
savedPath: await saveSpec(next, projectDir)
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
function pathSubdir(filePath) {
|
|
785
|
+
const parts = filePath.split(/[/\\]/);
|
|
786
|
+
const i = parts.lastIndexOf(SPECS_DIRNAME);
|
|
787
|
+
if (i < 0 || i + 1 >= parts.length) return void 0;
|
|
788
|
+
return parts[i + 1];
|
|
789
|
+
}
|
|
790
|
+
//#endregion
|
|
791
|
+
export { SPECS_DIRNAME, addActions, addActionsAndSave, createDocument, deleteDocument, extractAllDocuments, extractAppDocuments, extractDocumentModelDocuments, extractEditorDocuments, extractProcessorDocuments, extractSubgraphDocuments, generateAppFromDocument, generateDocumentModelFromDocument, generateEditorFromDocument, generateProcessorFromDocument, generateSubgraphFromDocument, getDocument, getDocumentModelSchema, getDocumentModels, getDocuments, getSpecEntry, listSpecDocumentTypes, saveSpec, specDir, specPath, stripLeadingDot, validateActions };
|
|
792
|
+
|
|
793
|
+
//# sourceMappingURL=index.js.map
|