@powerhousedao/codegen 6.0.0-dev.14 → 6.0.0-dev.140
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/src/templates/boilerplate/LICENSE.js → LICENSE} +1 -4
- package/dist/file-builders-DRzXbZTI.mjs +1653 -0
- package/dist/file-builders-DRzXbZTI.mjs.map +1 -0
- package/dist/index-CHAnPBj2.d.mts +199 -0
- package/dist/index-CHAnPBj2.d.mts.map +1 -0
- package/dist/index-CJZGVSYg.d.mts +65 -0
- package/dist/index-CJZGVSYg.d.mts.map +1 -0
- package/dist/index.d.mts +298 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +794 -0
- package/dist/index.mjs.map +1 -0
- package/dist/src/file-builders/index.d.mts +3 -0
- package/dist/src/file-builders/index.mjs +3 -0
- package/dist/src/name-builders/index.d.mts +2 -0
- package/dist/src/name-builders/index.mjs +146 -0
- package/dist/src/name-builders/index.mjs.map +1 -0
- package/dist/src/templates/index.d.mts +416 -0
- package/dist/src/templates/index.d.mts.map +1 -0
- package/dist/src/templates/index.mjs +2 -0
- package/dist/src/utils/index.d.mts +125 -0
- package/dist/src/utils/index.d.mts.map +1 -0
- package/dist/src/utils/index.mjs +380 -0
- package/dist/src/utils/index.mjs.map +1 -0
- package/dist/templates-DBvz_qPL.mjs +4611 -0
- package/dist/templates-DBvz_qPL.mjs.map +1 -0
- package/dist/types-e2ztuDtG.d.mts +87 -0
- package/dist/types-e2ztuDtG.d.mts.map +1 -0
- package/dist/validation-Bpg_44mW.d.mts +43 -0
- package/dist/validation-Bpg_44mW.d.mts.map +1 -0
- package/package.json +30 -59
- package/dist/index.d.ts +0 -8
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -7
- package/dist/index.js.map +0 -1
- package/dist/src/codegen/.hygen/package.json +0 -6
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-custom-subgraph/index.js +0 -10
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-custom-subgraph/resolvers.esm.t +0 -17
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-custom-subgraph/schema.esm.t +0 -16
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/actions.esm.t +0 -16
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/creators.esm.t +0 -9
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/customUtils.esm.t +0 -5
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/document-schema.esm.t +0 -56
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/document-type.esm.t +0 -6
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/documentModel.esm.t +0 -7
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/documentModelTest.esm.t +0 -121
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/hooks.esm.t +0 -49
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/index.esm.t +0 -23
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/index.js +0 -127
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/module.esm.t +0 -22
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/ph-factories.esm.t +0 -93
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/reducer.esm.t +0 -46
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/root-utils.esm.t +0 -11
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/rootActions.esm.t +0 -13
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/rootIndex.esm.t +0 -14
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/schema.esm.t +0 -6
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/src-index.esm.t +0 -5
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/types.esm.t +0 -31
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model/utils.esm.t +0 -77
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model-module/actions.esm.t +0 -22
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model-module/creators.esm.t +0 -37
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model-module/customTest.esm.t +0 -44
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model-module/error.esm.t +0 -36
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model-module/index.js +0 -71
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model-module/operations.esm.t +0 -17
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model-subgraph/index.js +0 -118
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model-subgraph/resolvers.esm.t +0 -130
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-document-model-subgraph/schema.esm.t +0 -44
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-drive-editor/components/CreateDocument.esm.t +0 -58
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-drive-editor/components/DriveContents.esm.t +0 -23
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-drive-editor/components/DriveExplorer.esm.t +0 -32
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-drive-editor/components/EmptyState.esm.t +0 -19
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-drive-editor/components/Files.esm.t +0 -29
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-drive-editor/components/FolderTree.esm.t +0 -108
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-drive-editor/components/Folders.esm.t +0 -28
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-drive-editor/components/NavigationBreadcrumbs.esm.t +0 -14
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-drive-editor/config.esm.t +0 -11
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-drive-editor/editor.esm.t +0 -18
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-drive-editor/index.js +0 -25
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-drive-editor/module.esm.t +0 -15
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-editor/editor.esm.t +0 -80
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-editor/index.js +0 -90
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-editor/module.esm.t +0 -16
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-import-script/index.esm.t +0 -59
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-import-script/index.js +0 -9
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-import-script/utils.esm.t +0 -100
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-analytics/factory.esm.t +0 -22
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-analytics/index.esm.t +0 -62
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-analytics/index.js +0 -12
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-analytics/index.root.esm.t +0 -10
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-analytics/index.root.inject.esm.t +0 -12
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-analytics/lib.esm.t +0 -34
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-analytics/lib.inject_call.esm.t +0 -7
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-analytics/lib.inject_export.esm.t +0 -7
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-analytics/lib.inject_import.esm.t +0 -7
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-relationalDb/factory.esm.t +0 -40
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-relationalDb/index.esm.t +0 -47
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-relationalDb/index.js +0 -12
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-relationalDb/index.root.esm.t +0 -10
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-relationalDb/index.root.inject.esm.t +0 -12
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-relationalDb/lib.esm.t +0 -34
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-relationalDb/lib.inject_call.esm.t +0 -7
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-relationalDb/lib.inject_export.esm.t +0 -9
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-relationalDb/lib.inject_import.esm.t +0 -7
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-relationalDb/migrations.esm.t +0 -23
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-processor-relationalDb/schema.esm.t +0 -13
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-subgraph/index.esm.t +0 -17
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-subgraph/index.js +0 -10
- package/dist/src/codegen/.hygen/templates/powerhouse/generate-subgraph/lib.esm.t +0 -9
- package/dist/src/codegen/__tests__/config.d.ts +0 -3
- package/dist/src/codegen/__tests__/config.d.ts.map +0 -1
- package/dist/src/codegen/__tests__/config.js +0 -3
- package/dist/src/codegen/__tests__/config.js.map +0 -1
- package/dist/src/codegen/__tests__/constants.d.ts +0 -16
- package/dist/src/codegen/__tests__/constants.d.ts.map +0 -1
- package/dist/src/codegen/__tests__/constants.js +0 -16
- package/dist/src/codegen/__tests__/constants.js.map +0 -1
- package/dist/src/codegen/__tests__/fixtures/expected-reducer-content-v3.d.ts +0 -2
- package/dist/src/codegen/__tests__/fixtures/expected-reducer-content-v3.d.ts.map +0 -1
- package/dist/src/codegen/__tests__/fixtures/expected-reducer-content-v3.js +0 -9
- package/dist/src/codegen/__tests__/fixtures/expected-reducer-content-v3.js.map +0 -1
- package/dist/src/codegen/__tests__/fixtures/expected-reducer-content.d.ts +0 -3
- package/dist/src/codegen/__tests__/fixtures/expected-reducer-content.d.ts.map +0 -1
- package/dist/src/codegen/__tests__/fixtures/expected-reducer-content.js +0 -33
- package/dist/src/codegen/__tests__/fixtures/expected-reducer-content.js.map +0 -1
- package/dist/src/codegen/__tests__/fixtures/run-generated-tests.d.ts +0 -2
- package/dist/src/codegen/__tests__/fixtures/run-generated-tests.d.ts.map +0 -1
- package/dist/src/codegen/__tests__/fixtures/run-generated-tests.js +0 -23
- package/dist/src/codegen/__tests__/fixtures/run-generated-tests.js.map +0 -1
- package/dist/src/codegen/__tests__/fixtures/typecheck.d.ts +0 -2
- package/dist/src/codegen/__tests__/fixtures/typecheck.d.ts.map +0 -1
- package/dist/src/codegen/__tests__/fixtures/typecheck.js +0 -23
- package/dist/src/codegen/__tests__/fixtures/typecheck.js.map +0 -1
- package/dist/src/codegen/__tests__/generate-doc-model.test.d.ts +0 -2
- package/dist/src/codegen/__tests__/generate-doc-model.test.d.ts.map +0 -1
- package/dist/src/codegen/__tests__/generate-doc-model.test.js +0 -233
- package/dist/src/codegen/__tests__/generate-doc-model.test.js.map +0 -1
- package/dist/src/codegen/__tests__/generate-drive-editor.test.d.ts +0 -2
- package/dist/src/codegen/__tests__/generate-drive-editor.test.d.ts.map +0 -1
- package/dist/src/codegen/__tests__/generate-drive-editor.test.js +0 -156
- package/dist/src/codegen/__tests__/generate-drive-editor.test.js.map +0 -1
- package/dist/src/codegen/__tests__/generate-editor.test.d.ts +0 -2
- package/dist/src/codegen/__tests__/generate-editor.test.d.ts.map +0 -1
- package/dist/src/codegen/__tests__/generate-editor.test.js +0 -115
- package/dist/src/codegen/__tests__/generate-editor.test.js.map +0 -1
- package/dist/src/codegen/__tests__/generate-manifest.test.d.ts +0 -2
- package/dist/src/codegen/__tests__/generate-manifest.test.d.ts.map +0 -1
- package/dist/src/codegen/__tests__/generate-manifest.test.js +0 -192
- package/dist/src/codegen/__tests__/generate-manifest.test.js.map +0 -1
- package/dist/src/codegen/__tests__/generate-versioned-document-model.test.d.ts +0 -2
- package/dist/src/codegen/__tests__/generate-versioned-document-model.test.d.ts.map +0 -1
- package/dist/src/codegen/__tests__/generate-versioned-document-model.test.js +0 -159
- package/dist/src/codegen/__tests__/generate-versioned-document-model.test.js.map +0 -1
- package/dist/src/codegen/__tests__/global-setup.d.ts +0 -2
- package/dist/src/codegen/__tests__/global-setup.d.ts.map +0 -1
- package/dist/src/codegen/__tests__/global-setup.js +0 -21
- package/dist/src/codegen/__tests__/global-setup.js.map +0 -1
- package/dist/src/codegen/__tests__/ts-morph-generator.test.d.ts +0 -2
- package/dist/src/codegen/__tests__/ts-morph-generator.test.d.ts.map +0 -1
- package/dist/src/codegen/__tests__/ts-morph-generator.test.js +0 -73
- package/dist/src/codegen/__tests__/ts-morph-generator.test.js.map +0 -1
- package/dist/src/codegen/__tests__/utils.d.ts +0 -7
- package/dist/src/codegen/__tests__/utils.d.ts.map +0 -1
- package/dist/src/codegen/__tests__/utils.js +0 -52
- package/dist/src/codegen/__tests__/utils.js.map +0 -1
- package/dist/src/codegen/generate.d.ts +0 -79
- package/dist/src/codegen/generate.d.ts.map +0 -1
- package/dist/src/codegen/generate.js +0 -334
- package/dist/src/codegen/generate.js.map +0 -1
- package/dist/src/codegen/graphql.d.ts +0 -36
- package/dist/src/codegen/graphql.d.ts.map +0 -1
- package/dist/src/codegen/graphql.js +0 -146
- package/dist/src/codegen/graphql.js.map +0 -1
- package/dist/src/codegen/hygen.d.ts +0 -47
- package/dist/src/codegen/hygen.d.ts.map +0 -1
- package/dist/src/codegen/hygen.js +0 -230
- package/dist/src/codegen/hygen.js.map +0 -1
- package/dist/src/codegen/index.d.ts +0 -7
- package/dist/src/codegen/index.d.ts.map +0 -1
- package/dist/src/codegen/index.js +0 -7
- package/dist/src/codegen/index.js.map +0 -1
- package/dist/src/codegen/kysely.d.ts +0 -6
- package/dist/src/codegen/kysely.d.ts.map +0 -1
- package/dist/src/codegen/kysely.js +0 -51
- package/dist/src/codegen/kysely.js.map +0 -1
- package/dist/src/codegen/types.d.ts +0 -9
- package/dist/src/codegen/types.d.ts.map +0 -1
- package/dist/src/codegen/types.js +0 -2
- package/dist/src/codegen/types.js.map +0 -1
- package/dist/src/codegen/utils.d.ts +0 -7
- package/dist/src/codegen/utils.d.ts.map +0 -1
- package/dist/src/codegen/utils.js +0 -79
- package/dist/src/codegen/utils.js.map +0 -1
- package/dist/src/create-lib/checkout-project.d.ts +0 -13
- package/dist/src/create-lib/checkout-project.d.ts.map +0 -1
- package/dist/src/create-lib/checkout-project.js +0 -47
- package/dist/src/create-lib/checkout-project.js.map +0 -1
- package/dist/src/create-lib/create-project.d.ts +0 -10
- package/dist/src/create-lib/create-project.d.ts.map +0 -1
- package/dist/src/create-lib/create-project.js +0 -98
- package/dist/src/create-lib/create-project.js.map +0 -1
- package/dist/src/create-lib/feature-flags.d.ts +0 -4
- package/dist/src/create-lib/feature-flags.d.ts.map +0 -1
- package/dist/src/create-lib/feature-flags.js +0 -4
- package/dist/src/create-lib/feature-flags.js.map +0 -1
- package/dist/src/create-lib/index.d.ts +0 -3
- package/dist/src/create-lib/index.d.ts.map +0 -1
- package/dist/src/create-lib/index.js +0 -3
- package/dist/src/create-lib/index.js.map +0 -1
- package/dist/src/create-lib/utils.d.ts +0 -8
- package/dist/src/create-lib/utils.d.ts.map +0 -1
- package/dist/src/create-lib/utils.js +0 -34
- package/dist/src/create-lib/utils.js.map +0 -1
- package/dist/src/file-builders/boilerplate/build-package-json.test.d.ts +0 -2
- package/dist/src/file-builders/boilerplate/build-package-json.test.d.ts.map +0 -1
- package/dist/src/file-builders/boilerplate/build-package-json.test.js +0 -28
- package/dist/src/file-builders/boilerplate/build-package-json.test.js.map +0 -1
- package/dist/src/file-builders/boilerplate/package-json-versions.test.d.ts +0 -2
- package/dist/src/file-builders/boilerplate/package-json-versions.test.d.ts.map +0 -1
- package/dist/src/file-builders/boilerplate/package-json-versions.test.js +0 -34
- package/dist/src/file-builders/boilerplate/package-json-versions.test.js.map +0 -1
- package/dist/src/file-builders/boilerplate/package.json.d.ts +0 -6
- package/dist/src/file-builders/boilerplate/package.json.d.ts.map +0 -1
- package/dist/src/file-builders/boilerplate/package.json.js +0 -18
- package/dist/src/file-builders/boilerplate/package.json.js.map +0 -1
- package/dist/src/file-builders/document-editor.d.ts +0 -8
- package/dist/src/file-builders/document-editor.d.ts.map +0 -1
- package/dist/src/file-builders/document-editor.js +0 -57
- package/dist/src/file-builders/document-editor.js.map +0 -1
- package/dist/src/file-builders/document-model/document-model.d.ts +0 -8
- package/dist/src/file-builders/document-model/document-model.d.ts.map +0 -1
- package/dist/src/file-builders/document-model/document-model.js +0 -246
- package/dist/src/file-builders/document-model/document-model.js.map +0 -1
- package/dist/src/file-builders/document-model/gen-dir.d.ts +0 -3
- package/dist/src/file-builders/document-model/gen-dir.d.ts.map +0 -1
- package/dist/src/file-builders/document-model/gen-dir.js +0 -201
- package/dist/src/file-builders/document-model/gen-dir.js.map +0 -1
- package/dist/src/file-builders/document-model/root-dir.d.ts +0 -3
- package/dist/src/file-builders/document-model/root-dir.d.ts.map +0 -1
- package/dist/src/file-builders/document-model/root-dir.js +0 -51
- package/dist/src/file-builders/document-model/root-dir.js.map +0 -1
- package/dist/src/file-builders/document-model/src-dir.d.ts +0 -3
- package/dist/src/file-builders/document-model/src-dir.d.ts.map +0 -1
- package/dist/src/file-builders/document-model/src-dir.js +0 -118
- package/dist/src/file-builders/document-model/src-dir.js.map +0 -1
- package/dist/src/file-builders/document-model/tests-dir.d.ts +0 -3
- package/dist/src/file-builders/document-model/tests-dir.d.ts.map +0 -1
- package/dist/src/file-builders/document-model/tests-dir.js +0 -127
- package/dist/src/file-builders/document-model/tests-dir.js.map +0 -1
- package/dist/src/file-builders/document-model/upgrades-dir.d.ts +0 -31
- package/dist/src/file-builders/document-model/upgrades-dir.d.ts.map +0 -1
- package/dist/src/file-builders/document-model/upgrades-dir.js +0 -135
- package/dist/src/file-builders/document-model/upgrades-dir.js.map +0 -1
- package/dist/src/file-builders/drive-editor.d.ts +0 -9
- package/dist/src/file-builders/drive-editor.d.ts.map +0 -1
- package/dist/src/file-builders/drive-editor.js +0 -164
- package/dist/src/file-builders/drive-editor.js.map +0 -1
- package/dist/src/file-builders/editor-common.d.ts +0 -13
- package/dist/src/file-builders/editor-common.d.ts.map +0 -1
- package/dist/src/file-builders/editor-common.js +0 -26
- package/dist/src/file-builders/editor-common.js.map +0 -1
- package/dist/src/file-builders/index-files.d.ts +0 -18
- package/dist/src/file-builders/index-files.d.ts.map +0 -1
- package/dist/src/file-builders/index-files.js +0 -25
- package/dist/src/file-builders/index-files.js.map +0 -1
- package/dist/src/file-builders/index.d.ts +0 -10
- package/dist/src/file-builders/index.d.ts.map +0 -1
- package/dist/src/file-builders/index.js +0 -9
- package/dist/src/file-builders/index.js.map +0 -1
- package/dist/src/file-builders/module-files.d.ts +0 -52
- package/dist/src/file-builders/module-files.d.ts.map +0 -1
- package/dist/src/file-builders/module-files.js +0 -230
- package/dist/src/file-builders/module-files.js.map +0 -1
- package/dist/src/file-builders/subgraphs.d.ts +0 -6
- package/dist/src/file-builders/subgraphs.d.ts.map +0 -1
- package/dist/src/file-builders/subgraphs.js +0 -53
- package/dist/src/file-builders/subgraphs.js.map +0 -1
- package/dist/src/file-builders/types.d.ts +0 -66
- package/dist/src/file-builders/types.d.ts.map +0 -1
- package/dist/src/file-builders/types.js +0 -2
- package/dist/src/file-builders/types.js.map +0 -1
- package/dist/src/index.d.ts +0 -5
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js +0 -5
- package/dist/src/index.js.map +0 -1
- package/dist/src/name-builders/get-action-names.d.ts +0 -6
- package/dist/src/name-builders/get-action-names.d.ts.map +0 -1
- package/dist/src/name-builders/get-action-names.js +0 -16
- package/dist/src/name-builders/get-action-names.js.map +0 -1
- package/dist/src/name-builders/get-variable-names.d.ts +0 -48
- package/dist/src/name-builders/get-variable-names.d.ts.map +0 -1
- package/dist/src/name-builders/get-variable-names.js +0 -147
- package/dist/src/name-builders/get-variable-names.js.map +0 -1
- package/dist/src/name-builders/index.d.ts +0 -3
- package/dist/src/name-builders/index.d.ts.map +0 -1
- package/dist/src/name-builders/index.js +0 -3
- package/dist/src/name-builders/index.js.map +0 -1
- package/dist/src/templates/boilerplate/AGENTS.md.d.ts +0 -2
- package/dist/src/templates/boilerplate/AGENTS.md.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/AGENTS.md.js +0 -408
- package/dist/src/templates/boilerplate/AGENTS.md.js.map +0 -1
- package/dist/src/templates/boilerplate/CLAUDE.md.d.ts +0 -2
- package/dist/src/templates/boilerplate/CLAUDE.md.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/CLAUDE.md.js +0 -408
- package/dist/src/templates/boilerplate/CLAUDE.md.js.map +0 -1
- package/dist/src/templates/boilerplate/LICENSE.d.ts +0 -2
- package/dist/src/templates/boilerplate/LICENSE.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/LICENSE.js.map +0 -1
- package/dist/src/templates/boilerplate/README.md.d.ts +0 -2
- package/dist/src/templates/boilerplate/README.md.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/README.md.js +0 -194
- package/dist/src/templates/boilerplate/README.md.js.map +0 -1
- package/dist/src/templates/boilerplate/claude/settings.local.json.d.ts +0 -2
- package/dist/src/templates/boilerplate/claude/settings.local.json.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/claude/settings.local.json.js +0 -18
- package/dist/src/templates/boilerplate/claude/settings.local.json.js.map +0 -1
- package/dist/src/templates/boilerplate/cursor/mcp.json.d.ts +0 -2
- package/dist/src/templates/boilerplate/cursor/mcp.json.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/cursor/mcp.json.js +0 -12
- package/dist/src/templates/boilerplate/cursor/mcp.json.js.map +0 -1
- package/dist/src/templates/boilerplate/document-models/document-models.d.ts +0 -2
- package/dist/src/templates/boilerplate/document-models/document-models.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/document-models/document-models.js +0 -7
- package/dist/src/templates/boilerplate/document-models/document-models.js.map +0 -1
- package/dist/src/templates/boilerplate/document-models/index.d.ts +0 -2
- package/dist/src/templates/boilerplate/document-models/index.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/document-models/index.js +0 -2
- package/dist/src/templates/boilerplate/document-models/index.js.map +0 -1
- package/dist/src/templates/boilerplate/editors/editors.d.ts +0 -2
- package/dist/src/templates/boilerplate/editors/editors.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/editors/editors.js +0 -7
- package/dist/src/templates/boilerplate/editors/editors.js.map +0 -1
- package/dist/src/templates/boilerplate/editors/index.d.ts +0 -2
- package/dist/src/templates/boilerplate/editors/index.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/editors/index.js +0 -2
- package/dist/src/templates/boilerplate/editors/index.js.map +0 -1
- package/dist/src/templates/boilerplate/eslint.config.js.d.ts +0 -2
- package/dist/src/templates/boilerplate/eslint.config.js.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/eslint.config.js.js +0 -140
- package/dist/src/templates/boilerplate/eslint.config.js.js.map +0 -1
- package/dist/src/templates/boilerplate/gemini/settings.json.d.ts +0 -2
- package/dist/src/templates/boilerplate/gemini/settings.json.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/gemini/settings.json.js +0 -12
- package/dist/src/templates/boilerplate/gemini/settings.json.js.map +0 -1
- package/dist/src/templates/boilerplate/gitignore.d.ts +0 -2
- package/dist/src/templates/boilerplate/gitignore.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/gitignore.js +0 -11
- package/dist/src/templates/boilerplate/gitignore.js.map +0 -1
- package/dist/src/templates/boilerplate/index.d.ts +0 -2
- package/dist/src/templates/boilerplate/index.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/index.html.d.ts +0 -2
- package/dist/src/templates/boilerplate/index.html.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/index.html.js +0 -31
- package/dist/src/templates/boilerplate/index.html.js.map +0 -1
- package/dist/src/templates/boilerplate/index.html.legacy.d.ts +0 -2
- package/dist/src/templates/boilerplate/index.html.legacy.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/index.html.legacy.js +0 -36
- package/dist/src/templates/boilerplate/index.html.legacy.js.map +0 -1
- package/dist/src/templates/boilerplate/index.js +0 -8
- package/dist/src/templates/boilerplate/index.js.map +0 -1
- package/dist/src/templates/boilerplate/mcp.json.d.ts +0 -2
- package/dist/src/templates/boilerplate/mcp.json.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/mcp.json.js +0 -12
- package/dist/src/templates/boilerplate/mcp.json.js.map +0 -1
- package/dist/src/templates/boilerplate/npmrc.d.ts +0 -2
- package/dist/src/templates/boilerplate/npmrc.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/npmrc.js +0 -2
- package/dist/src/templates/boilerplate/npmrc.js.map +0 -1
- package/dist/src/templates/boilerplate/package.json.d.ts +0 -2
- package/dist/src/templates/boilerplate/package.json.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/package.json.js +0 -115
- package/dist/src/templates/boilerplate/package.json.js.map +0 -1
- package/dist/src/templates/boilerplate/package.json.legacy.d.ts +0 -55
- package/dist/src/templates/boilerplate/package.json.legacy.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/package.json.legacy.js +0 -55
- package/dist/src/templates/boilerplate/package.json.legacy.js.map +0 -1
- package/dist/src/templates/boilerplate/powerhouse.config.json.d.ts +0 -6
- package/dist/src/templates/boilerplate/powerhouse.config.json.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/powerhouse.config.json.js +0 -46
- package/dist/src/templates/boilerplate/powerhouse.config.json.js.map +0 -1
- package/dist/src/templates/boilerplate/powerhouse.manifest.json.d.ts +0 -2
- package/dist/src/templates/boilerplate/powerhouse.manifest.json.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/powerhouse.manifest.json.js +0 -19
- package/dist/src/templates/boilerplate/powerhouse.manifest.json.js.map +0 -1
- package/dist/src/templates/boilerplate/processors/index.d.ts +0 -2
- package/dist/src/templates/boilerplate/processors/index.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/processors/index.js +0 -2
- package/dist/src/templates/boilerplate/processors/index.js.map +0 -1
- package/dist/src/templates/boilerplate/style.css.d.ts +0 -2
- package/dist/src/templates/boilerplate/style.css.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/style.css.js +0 -17
- package/dist/src/templates/boilerplate/style.css.js.map +0 -1
- package/dist/src/templates/boilerplate/subgraphs/index.d.ts +0 -2
- package/dist/src/templates/boilerplate/subgraphs/index.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/subgraphs/index.js +0 -2
- package/dist/src/templates/boilerplate/subgraphs/index.js.map +0 -1
- package/dist/src/templates/boilerplate/tsconfig.json.d.ts +0 -2
- package/dist/src/templates/boilerplate/tsconfig.json.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/tsconfig.json.js +0 -46
- package/dist/src/templates/boilerplate/tsconfig.json.js.map +0 -1
- package/dist/src/templates/boilerplate/vite.config.ts.d.ts +0 -2
- package/dist/src/templates/boilerplate/vite.config.ts.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/vite.config.ts.js +0 -21
- package/dist/src/templates/boilerplate/vite.config.ts.js.map +0 -1
- package/dist/src/templates/boilerplate/vitest.config.ts.d.ts +0 -2
- package/dist/src/templates/boilerplate/vitest.config.ts.d.ts.map +0 -1
- package/dist/src/templates/boilerplate/vitest.config.ts.js +0 -21
- package/dist/src/templates/boilerplate/vitest.config.ts.js.map +0 -1
- package/dist/src/templates/document-editor/editor.d.ts +0 -5
- package/dist/src/templates/document-editor/editor.d.ts.map +0 -1
- package/dist/src/templates/document-editor/editor.js +0 -85
- package/dist/src/templates/document-editor/editor.js.map +0 -1
- package/dist/src/templates/document-editor/module.d.ts +0 -7
- package/dist/src/templates/document-editor/module.d.ts.map +0 -1
- package/dist/src/templates/document-editor/module.js +0 -16
- package/dist/src/templates/document-editor/module.js.map +0 -1
- package/dist/src/templates/document-model/actions.d.ts +0 -3
- package/dist/src/templates/document-model/actions.d.ts.map +0 -1
- package/dist/src/templates/document-model/actions.js +0 -28
- package/dist/src/templates/document-model/actions.js.map +0 -1
- package/dist/src/templates/document-model/gen/actions.d.ts +0 -3
- package/dist/src/templates/document-model/gen/actions.d.ts.map +0 -1
- package/dist/src/templates/document-model/gen/actions.js +0 -40
- package/dist/src/templates/document-model/gen/actions.js.map +0 -1
- package/dist/src/templates/document-model/gen/creators.d.ts +0 -3
- package/dist/src/templates/document-model/gen/creators.d.ts.map +0 -1
- package/dist/src/templates/document-model/gen/creators.js +0 -16
- package/dist/src/templates/document-model/gen/creators.js.map +0 -1
- package/dist/src/templates/document-model/gen/document-schema.d.ts +0 -3
- package/dist/src/templates/document-model/gen/document-schema.d.ts.map +0 -1
- package/dist/src/templates/document-model/gen/document-schema.js +0 -56
- package/dist/src/templates/document-model/gen/document-schema.js.map +0 -1
- package/dist/src/templates/document-model/gen/document-type.d.ts +0 -3
- package/dist/src/templates/document-model/gen/document-type.d.ts.map +0 -1
- package/dist/src/templates/document-model/gen/document-type.js +0 -5
- package/dist/src/templates/document-model/gen/document-type.js.map +0 -1
- package/dist/src/templates/document-model/gen/index.d.ts +0 -3
- package/dist/src/templates/document-model/gen/index.d.ts.map +0 -1
- package/dist/src/templates/document-model/gen/index.js +0 -29
- package/dist/src/templates/document-model/gen/index.js.map +0 -1
- package/dist/src/templates/document-model/gen/modules/actions.d.ts +0 -8
- package/dist/src/templates/document-model/gen/modules/actions.d.ts.map +0 -1
- package/dist/src/templates/document-model/gen/modules/actions.js +0 -39
- package/dist/src/templates/document-model/gen/modules/actions.js.map +0 -1
- package/dist/src/templates/document-model/gen/modules/creators.d.ts +0 -3
- package/dist/src/templates/document-model/gen/modules/creators.d.ts.map +0 -1
- package/dist/src/templates/document-model/gen/modules/creators.js +0 -97
- package/dist/src/templates/document-model/gen/modules/creators.js.map +0 -1
- package/dist/src/templates/document-model/gen/modules/error.d.ts +0 -3
- package/dist/src/templates/document-model/gen/modules/error.d.ts.map +0 -1
- package/dist/src/templates/document-model/gen/modules/error.js +0 -73
- package/dist/src/templates/document-model/gen/modules/error.js.map +0 -1
- package/dist/src/templates/document-model/gen/modules/operations.d.ts +0 -3
- package/dist/src/templates/document-model/gen/modules/operations.d.ts.map +0 -1
- package/dist/src/templates/document-model/gen/modules/operations.js +0 -61
- package/dist/src/templates/document-model/gen/modules/operations.js.map +0 -1
- package/dist/src/templates/document-model/gen/ph-factories.d.ts +0 -3
- package/dist/src/templates/document-model/gen/ph-factories.d.ts.map +0 -1
- package/dist/src/templates/document-model/gen/ph-factories.js +0 -93
- package/dist/src/templates/document-model/gen/ph-factories.js.map +0 -1
- package/dist/src/templates/document-model/gen/reducer.d.ts +0 -3
- package/dist/src/templates/document-model/gen/reducer.d.ts.map +0 -1
- package/dist/src/templates/document-model/gen/reducer.js +0 -106
- package/dist/src/templates/document-model/gen/reducer.js.map +0 -1
- package/dist/src/templates/document-model/gen/schema/index.d.ts +0 -2
- package/dist/src/templates/document-model/gen/schema/index.d.ts.map +0 -1
- package/dist/src/templates/document-model/gen/schema/index.js +0 -6
- package/dist/src/templates/document-model/gen/schema/index.js.map +0 -1
- package/dist/src/templates/document-model/gen/types.d.ts +0 -3
- package/dist/src/templates/document-model/gen/types.d.ts.map +0 -1
- package/dist/src/templates/document-model/gen/types.js +0 -38
- package/dist/src/templates/document-model/gen/types.js.map +0 -1
- package/dist/src/templates/document-model/gen/utils.d.ts +0 -3
- package/dist/src/templates/document-model/gen/utils.d.ts.map +0 -1
- package/dist/src/templates/document-model/gen/utils.js +0 -77
- package/dist/src/templates/document-model/gen/utils.js.map +0 -1
- package/dist/src/templates/document-model/hooks.d.ts +0 -3
- package/dist/src/templates/document-model/hooks.d.ts.map +0 -1
- package/dist/src/templates/document-model/hooks.js +0 -52
- package/dist/src/templates/document-model/hooks.js.map +0 -1
- package/dist/src/templates/document-model/index.d.ts +0 -2
- package/dist/src/templates/document-model/index.d.ts.map +0 -1
- package/dist/src/templates/document-model/index.js +0 -9
- package/dist/src/templates/document-model/index.js.map +0 -1
- package/dist/src/templates/document-model/module.d.ts +0 -9
- package/dist/src/templates/document-model/module.d.ts.map +0 -1
- package/dist/src/templates/document-model/module.js +0 -26
- package/dist/src/templates/document-model/module.js.map +0 -1
- package/dist/src/templates/document-model/src/index.d.ts +0 -2
- package/dist/src/templates/document-model/src/index.d.ts.map +0 -1
- package/dist/src/templates/document-model/src/index.js +0 -5
- package/dist/src/templates/document-model/src/index.js.map +0 -1
- package/dist/src/templates/document-model/src/tests/document-model.test.d.ts +0 -3
- package/dist/src/templates/document-model/src/tests/document-model.test.d.ts.map +0 -1
- package/dist/src/templates/document-model/src/tests/document-model.test.js +0 -121
- package/dist/src/templates/document-model/src/tests/document-model.test.js.map +0 -1
- package/dist/src/templates/document-model/src/tests/module.test.d.ts +0 -6
- package/dist/src/templates/document-model/src/tests/module.test.d.ts.map +0 -1
- package/dist/src/templates/document-model/src/tests/module.test.js +0 -87
- package/dist/src/templates/document-model/src/tests/module.test.js.map +0 -1
- package/dist/src/templates/document-model/src/utils.d.ts +0 -2
- package/dist/src/templates/document-model/src/utils.d.ts.map +0 -1
- package/dist/src/templates/document-model/src/utils.js +0 -5
- package/dist/src/templates/document-model/src/utils.js.map +0 -1
- package/dist/src/templates/document-model/upgrades/upgrade-transition.d.ts +0 -7
- package/dist/src/templates/document-model/upgrades/upgrade-transition.d.ts.map +0 -1
- package/dist/src/templates/document-model/upgrades/upgrade-transition.js +0 -22
- package/dist/src/templates/document-model/upgrades/upgrade-transition.js.map +0 -1
- package/dist/src/templates/document-model/utils.d.ts +0 -3
- package/dist/src/templates/document-model/utils.d.ts.map +0 -1
- package/dist/src/templates/document-model/utils.js +0 -11
- package/dist/src/templates/document-model/utils.js.map +0 -1
- package/dist/src/templates/drive-editor/components/CreateDocument.d.ts +0 -2
- package/dist/src/templates/drive-editor/components/CreateDocument.d.ts.map +0 -1
- package/dist/src/templates/drive-editor/components/CreateDocument.js +0 -58
- package/dist/src/templates/drive-editor/components/CreateDocument.js.map +0 -1
- package/dist/src/templates/drive-editor/components/DriveContents.d.ts +0 -2
- package/dist/src/templates/drive-editor/components/DriveContents.d.ts.map +0 -1
- package/dist/src/templates/drive-editor/components/DriveContents.js +0 -24
- package/dist/src/templates/drive-editor/components/DriveContents.js.map +0 -1
- package/dist/src/templates/drive-editor/components/DriveExplorer.d.ts +0 -2
- package/dist/src/templates/drive-editor/components/DriveExplorer.d.ts.map +0 -1
- package/dist/src/templates/drive-editor/components/DriveExplorer.js +0 -32
- package/dist/src/templates/drive-editor/components/DriveExplorer.js.map +0 -1
- package/dist/src/templates/drive-editor/components/EmptyState.d.ts +0 -2
- package/dist/src/templates/drive-editor/components/EmptyState.d.ts.map +0 -1
- package/dist/src/templates/drive-editor/components/EmptyState.js +0 -19
- package/dist/src/templates/drive-editor/components/EmptyState.js.map +0 -1
- package/dist/src/templates/drive-editor/components/Files.d.ts +0 -2
- package/dist/src/templates/drive-editor/components/Files.d.ts.map +0 -1
- package/dist/src/templates/drive-editor/components/Files.js +0 -30
- package/dist/src/templates/drive-editor/components/Files.js.map +0 -1
- package/dist/src/templates/drive-editor/components/FolderTree.d.ts +0 -2
- package/dist/src/templates/drive-editor/components/FolderTree.d.ts.map +0 -1
- package/dist/src/templates/drive-editor/components/FolderTree.js +0 -108
- package/dist/src/templates/drive-editor/components/FolderTree.js.map +0 -1
- package/dist/src/templates/drive-editor/components/Folders.d.ts +0 -2
- package/dist/src/templates/drive-editor/components/Folders.d.ts.map +0 -1
- package/dist/src/templates/drive-editor/components/Folders.js +0 -28
- package/dist/src/templates/drive-editor/components/Folders.js.map +0 -1
- package/dist/src/templates/drive-editor/components/NavigationBreadcrumbs.d.ts +0 -2
- package/dist/src/templates/drive-editor/components/NavigationBreadcrumbs.d.ts.map +0 -1
- package/dist/src/templates/drive-editor/components/NavigationBreadcrumbs.js +0 -14
- package/dist/src/templates/drive-editor/components/NavigationBreadcrumbs.js.map +0 -1
- package/dist/src/templates/drive-editor/config.d.ts +0 -5
- package/dist/src/templates/drive-editor/config.d.ts.map +0 -1
- package/dist/src/templates/drive-editor/config.js +0 -11
- package/dist/src/templates/drive-editor/config.js.map +0 -1
- package/dist/src/templates/drive-editor/editor.d.ts +0 -2
- package/dist/src/templates/drive-editor/editor.d.ts.map +0 -1
- package/dist/src/templates/drive-editor/editor.js +0 -18
- package/dist/src/templates/drive-editor/editor.js.map +0 -1
- package/dist/src/templates/index.d.ts +0 -65
- package/dist/src/templates/index.d.ts.map +0 -1
- package/dist/src/templates/index.js +0 -65
- package/dist/src/templates/index.js.map +0 -1
- package/dist/src/ts-morph-generator/__tests__/ReducerGenerator.test.d.ts +0 -2
- package/dist/src/ts-morph-generator/__tests__/ReducerGenerator.test.d.ts.map +0 -1
- package/dist/src/ts-morph-generator/__tests__/ReducerGenerator.test.js +0 -491
- package/dist/src/ts-morph-generator/__tests__/ReducerGenerator.test.js.map +0 -1
- package/dist/src/ts-morph-generator/core/FileGenerator.d.ts +0 -11
- package/dist/src/ts-morph-generator/core/FileGenerator.d.ts.map +0 -1
- package/dist/src/ts-morph-generator/core/FileGenerator.js +0 -11
- package/dist/src/ts-morph-generator/core/FileGenerator.js.map +0 -1
- package/dist/src/ts-morph-generator/core/GenerationContext.d.ts +0 -39
- package/dist/src/ts-morph-generator/core/GenerationContext.d.ts.map +0 -1
- package/dist/src/ts-morph-generator/core/GenerationContext.js +0 -2
- package/dist/src/ts-morph-generator/core/GenerationContext.js.map +0 -1
- package/dist/src/ts-morph-generator/core/ReducerGenerator.d.ts +0 -12
- package/dist/src/ts-morph-generator/core/ReducerGenerator.d.ts.map +0 -1
- package/dist/src/ts-morph-generator/core/ReducerGenerator.js +0 -147
- package/dist/src/ts-morph-generator/core/ReducerGenerator.js.map +0 -1
- package/dist/src/ts-morph-generator/core/TSMorphCodeGenerator.d.ts +0 -20
- package/dist/src/ts-morph-generator/core/TSMorphCodeGenerator.d.ts.map +0 -1
- package/dist/src/ts-morph-generator/core/TSMorphCodeGenerator.js +0 -120
- package/dist/src/ts-morph-generator/core/TSMorphCodeGenerator.js.map +0 -1
- package/dist/src/ts-morph-generator/index.d.ts +0 -4
- package/dist/src/ts-morph-generator/index.d.ts.map +0 -1
- package/dist/src/ts-morph-generator/index.js +0 -4
- package/dist/src/ts-morph-generator/index.js.map +0 -1
- package/dist/src/ts-morph-generator/utilities/DeclarationManager.d.ts +0 -5
- package/dist/src/ts-morph-generator/utilities/DeclarationManager.d.ts.map +0 -1
- package/dist/src/ts-morph-generator/utilities/DeclarationManager.js +0 -10
- package/dist/src/ts-morph-generator/utilities/DeclarationManager.js.map +0 -1
- package/dist/src/ts-morph-generator/utilities/DirectoryManager.d.ts +0 -13
- package/dist/src/ts-morph-generator/utilities/DirectoryManager.d.ts.map +0 -1
- package/dist/src/ts-morph-generator/utilities/DirectoryManager.js +0 -45
- package/dist/src/ts-morph-generator/utilities/DirectoryManager.js.map +0 -1
- package/dist/src/ts-morph-generator/utilities/ImportManager.d.ts +0 -15
- package/dist/src/ts-morph-generator/utilities/ImportManager.d.ts.map +0 -1
- package/dist/src/ts-morph-generator/utilities/ImportManager.js +0 -50
- package/dist/src/ts-morph-generator/utilities/ImportManager.js.map +0 -1
- package/dist/src/ts-morph-generator/utilities/index.d.ts +0 -4
- package/dist/src/ts-morph-generator/utilities/index.d.ts.map +0 -1
- package/dist/src/ts-morph-generator/utilities/index.js +0 -4
- package/dist/src/ts-morph-generator/utilities/index.js.map +0 -1
- package/dist/src/utils/cli.d.ts +0 -26
- package/dist/src/utils/cli.d.ts.map +0 -1
- package/dist/src/utils/cli.js +0 -57
- package/dist/src/utils/cli.js.map +0 -1
- package/dist/src/utils/constants.d.ts +0 -13
- package/dist/src/utils/constants.d.ts.map +0 -1
- package/dist/src/utils/constants.js +0 -29
- package/dist/src/utils/constants.js.map +0 -1
- package/dist/src/utils/dependencies.d.ts +0 -12
- package/dist/src/utils/dependencies.d.ts.map +0 -1
- package/dist/src/utils/dependencies.js +0 -34
- package/dist/src/utils/dependencies.js.map +0 -1
- package/dist/src/utils/document-type-metadata.d.ts +0 -15
- package/dist/src/utils/document-type-metadata.d.ts.map +0 -1
- package/dist/src/utils/document-type-metadata.js +0 -67
- package/dist/src/utils/document-type-metadata.js.map +0 -1
- package/dist/src/utils/format-with-prettier.d.ts +0 -5
- package/dist/src/utils/format-with-prettier.d.ts.map +0 -1
- package/dist/src/utils/format-with-prettier.js +0 -20
- package/dist/src/utils/format-with-prettier.js.map +0 -1
- package/dist/src/utils/index.d.ts +0 -14
- package/dist/src/utils/index.d.ts.map +0 -1
- package/dist/src/utils/index.js +0 -14
- package/dist/src/utils/index.js.map +0 -1
- package/dist/src/utils/mock.d.ts +0 -3
- package/dist/src/utils/mock.d.ts.map +0 -1
- package/dist/src/utils/mock.js +0 -5
- package/dist/src/utils/mock.js.map +0 -1
- package/dist/src/utils/source-files.d.ts +0 -18
- package/dist/src/utils/source-files.d.ts.map +0 -1
- package/dist/src/utils/source-files.js +0 -39
- package/dist/src/utils/source-files.js.map +0 -1
- package/dist/src/utils/spawn-async.d.ts +0 -5
- package/dist/src/utils/spawn-async.d.ts.map +0 -1
- package/dist/src/utils/spawn-async.js +0 -29
- package/dist/src/utils/spawn-async.js.map +0 -1
- package/dist/src/utils/syntax-builders.d.ts +0 -8
- package/dist/src/utils/syntax-builders.d.ts.map +0 -1
- package/dist/src/utils/syntax-builders.js +0 -72
- package/dist/src/utils/syntax-builders.js.map +0 -1
- package/dist/src/utils/syntax-getters.d.ts +0 -9
- package/dist/src/utils/syntax-getters.d.ts.map +0 -1
- package/dist/src/utils/syntax-getters.js +0 -20
- package/dist/src/utils/syntax-getters.js.map +0 -1
- package/dist/src/utils/ts-morph-project.d.ts +0 -15
- package/dist/src/utils/ts-morph-project.d.ts.map +0 -1
- package/dist/src/utils/ts-morph-project.js +0 -28
- package/dist/src/utils/ts-morph-project.js.map +0 -1
- package/dist/src/utils/unsafe-utils.d.ts +0 -8
- package/dist/src/utils/unsafe-utils.d.ts.map +0 -1
- package/dist/src/utils/unsafe-utils.js +0 -23
- package/dist/src/utils/unsafe-utils.js.map +0 -1
- package/dist/src/utils/validation.d.ts +0 -18
- package/dist/src/utils/validation.d.ts.map +0 -1
- package/dist/src/utils/validation.js +0 -138
- package/dist/src/utils/validation.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/vitest.config.d.ts +0 -3
- package/dist/vitest.config.d.ts.map +0 -1
- package/dist/vitest.config.js +0 -19
- package/dist/vitest.config.js.map +0 -1
|
@@ -0,0 +1,4611 @@
|
|
|
1
|
+
import { getActionInputName, getActionInputTypeNames, getActionType, getActionTypeName } from "./src/name-builders/index.mjs";
|
|
2
|
+
import { camelCase, capitalCase, constantCase, kebabCase, pascalCase } from "change-case";
|
|
3
|
+
import { css, html, js, json, md, ts, tsx, yaml } from "@tmpl/core";
|
|
4
|
+
//#region src/templates/app/components/CreateDocument.ts
|
|
5
|
+
const createDocumentFileTemplate = tsx`
|
|
6
|
+
import type { VetraDocumentModelModule } from "@powerhousedao/reactor-browser";
|
|
7
|
+
import {
|
|
8
|
+
showCreateDocumentModal,
|
|
9
|
+
useAllowedDocumentModelModules,
|
|
10
|
+
} from "@powerhousedao/reactor-browser";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Document creation UI component.
|
|
14
|
+
* Displays available document types as clickable buttons.
|
|
15
|
+
*/
|
|
16
|
+
export function CreateDocument() {
|
|
17
|
+
const allowedDocumentModelModules = useAllowedDocumentModelModules();
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div>
|
|
21
|
+
{/* Customize section title here */}
|
|
22
|
+
<h3 className="mb-3 mt-4 text-sm font-bold text-gray-600">
|
|
23
|
+
Create document
|
|
24
|
+
</h3>
|
|
25
|
+
{/* Customize layout by changing flex-wrap, gap, or grid layout */}
|
|
26
|
+
<div className="flex w-full flex-wrap gap-4">
|
|
27
|
+
{allowedDocumentModelModules?.map((documentModelModule) => {
|
|
28
|
+
return (
|
|
29
|
+
<CreateDocumentButton
|
|
30
|
+
key={documentModelModule.documentModel.global.id}
|
|
31
|
+
documentModelModule={documentModelModule}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
})}
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type Props = {
|
|
41
|
+
documentModelModule: VetraDocumentModelModule;
|
|
42
|
+
};
|
|
43
|
+
function CreateDocumentButton({ documentModelModule }: Props) {
|
|
44
|
+
const documentType = documentModelModule.documentModel.global.id;
|
|
45
|
+
const documentModelName =
|
|
46
|
+
documentModelModule.documentModel.global.name || documentType;
|
|
47
|
+
const documentModelDescription =
|
|
48
|
+
documentModelModule.documentModel.global.description;
|
|
49
|
+
return (
|
|
50
|
+
<button
|
|
51
|
+
className="cursor-pointer rounded-md bg-gray-200 py-2 px-3 hover:bg-gray-300"
|
|
52
|
+
title={documentModelName}
|
|
53
|
+
aria-description={documentModelDescription}
|
|
54
|
+
onClick={() => showCreateDocumentModal(documentType)}
|
|
55
|
+
>
|
|
56
|
+
{documentModelName}
|
|
57
|
+
</button>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
`.raw;
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/templates/app/components/DriveContents.ts
|
|
63
|
+
const appDriveContentsFileTemplate = () => tsx`
|
|
64
|
+
import { CreateDocument } from "./CreateDocument.js";
|
|
65
|
+
import { EmptyState } from "./EmptyState.js";
|
|
66
|
+
import { Files } from "./Files.js";
|
|
67
|
+
import { Folders } from "./Folders.js";
|
|
68
|
+
import { NavigationBreadcrumbs } from "./NavigationBreadcrumbs.js";
|
|
69
|
+
|
|
70
|
+
/** Shows the documents and folders in the selected drive */
|
|
71
|
+
export function DriveContents() {
|
|
72
|
+
return (
|
|
73
|
+
<div className="space-y-6 px-6">
|
|
74
|
+
<NavigationBreadcrumbs />
|
|
75
|
+
<Folders />
|
|
76
|
+
<Files />
|
|
77
|
+
<EmptyState />
|
|
78
|
+
<CreateDocument />
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
`.raw;
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/templates/app/components/DriveExplorer.ts
|
|
87
|
+
const driveExplorerFileTemplate = tsx`
|
|
88
|
+
import type { EditorProps } from "document-model";
|
|
89
|
+
import { FolderTree } from "./FolderTree.js";
|
|
90
|
+
import { DriveContents } from "./DriveContents.js";
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Main drive explorer component with sidebar navigation and content area.
|
|
94
|
+
* Layout: Left sidebar (folder tree) + Right content area (files/folders + document editor)
|
|
95
|
+
*/
|
|
96
|
+
export function DriveExplorer({ children }: EditorProps) {
|
|
97
|
+
// if a document is selected then it's editor will be passed as children
|
|
98
|
+
const showDocumentEditor = !!children;
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<div className="flex h-full">
|
|
102
|
+
<FolderTree />
|
|
103
|
+
<div className="flex-1 overflow-y-auto p-4">
|
|
104
|
+
{/* Conditional rendering: Document editor or folder contents */}
|
|
105
|
+
{showDocumentEditor ? (
|
|
106
|
+
/* Document editor view */
|
|
107
|
+
children
|
|
108
|
+
) : (
|
|
109
|
+
/* Folder contents view */
|
|
110
|
+
<DriveContents />
|
|
111
|
+
)}
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
`.raw;
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/templates/app/components/EmptyState.ts
|
|
119
|
+
const emptyStateFileTemplate = tsx`
|
|
120
|
+
import { useNodesInSelectedDriveOrFolder } from "@powerhousedao/reactor-browser";
|
|
121
|
+
|
|
122
|
+
/** Shows a message when the selected drive or folder is empty */
|
|
123
|
+
export function EmptyState() {
|
|
124
|
+
const nodes = useNodesInSelectedDriveOrFolder();
|
|
125
|
+
const hasNodes = nodes.length > 0;
|
|
126
|
+
if (hasNodes) return null;
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<div className="py-12 text-center text-gray-500">
|
|
130
|
+
<p className="text-lg">This folder is empty</p>
|
|
131
|
+
<p className="mt-2 text-sm">Create your first document or folder below</p>
|
|
132
|
+
</div>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
`.raw;
|
|
136
|
+
//#endregion
|
|
137
|
+
//#region src/templates/app/components/Files.ts
|
|
138
|
+
const appFilesFileTemplate = () => tsx`
|
|
139
|
+
import { FileItem } from "@powerhousedao/design-system/connect";
|
|
140
|
+
import {
|
|
141
|
+
useNodesInSelectedDriveOrFolder,
|
|
142
|
+
isFileNodeKind,
|
|
143
|
+
} from "@powerhousedao/reactor-browser";
|
|
144
|
+
|
|
145
|
+
/** Shows the files in the selected drive or folder */
|
|
146
|
+
export function Files() {
|
|
147
|
+
const nodes = useNodesInSelectedDriveOrFolder();
|
|
148
|
+
const fileNodes = nodes.filter((n) => isFileNodeKind(n));
|
|
149
|
+
const hasFiles = fileNodes.length > 0;
|
|
150
|
+
|
|
151
|
+
if (!hasFiles) return null;
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
<div>
|
|
155
|
+
<h3 className="mb-2 text-sm font-semibold text-gray-600">Documents</h3>
|
|
156
|
+
<div className="flex flex-wrap gap-4">
|
|
157
|
+
{fileNodes.map((fileNode) => (
|
|
158
|
+
<FileItem key={fileNode.id} fileNode={fileNode} />
|
|
159
|
+
))}
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
`.raw;
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/templates/app/components/Folders.ts
|
|
168
|
+
const appFoldersFileTemplate = () => tsx`
|
|
169
|
+
import { FolderItem } from "@powerhousedao/design-system/connect";
|
|
170
|
+
import {
|
|
171
|
+
useNodesInSelectedDriveOrFolder,
|
|
172
|
+
isFolderNodeKind,
|
|
173
|
+
} from "@powerhousedao/reactor-browser";
|
|
174
|
+
|
|
175
|
+
/** Shows the folders in the selected drive or folder */
|
|
176
|
+
export function Folders() {
|
|
177
|
+
const nodes = useNodesInSelectedDriveOrFolder();
|
|
178
|
+
const folderNodes = nodes.filter((n) => isFolderNodeKind(n));
|
|
179
|
+
const hasFolders = folderNodes.length > 0;
|
|
180
|
+
if (!hasFolders) return null;
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<div>
|
|
184
|
+
<h3 className="mb-2 text-sm font-bold text-gray-600">Folders</h3>
|
|
185
|
+
<div className="flex flex-wrap gap-4">
|
|
186
|
+
{folderNodes.map((folderNode) => (
|
|
187
|
+
<FolderItem key={folderNode.id} folderNode={folderNode} />
|
|
188
|
+
))}
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
`.raw;
|
|
194
|
+
//#endregion
|
|
195
|
+
//#region src/templates/app/components/FolderTree.ts
|
|
196
|
+
const folderTreeFileTemplate = tsx`
|
|
197
|
+
import {
|
|
198
|
+
Sidebar,
|
|
199
|
+
SidebarProvider,
|
|
200
|
+
type SidebarNode,
|
|
201
|
+
} from "@powerhousedao/document-engineering";
|
|
202
|
+
import {
|
|
203
|
+
setSelectedNode,
|
|
204
|
+
useNodesInSelectedDrive,
|
|
205
|
+
useSelectedDrive,
|
|
206
|
+
useSelectedNode,
|
|
207
|
+
} from "@powerhousedao/reactor-browser";
|
|
208
|
+
import type { Node } from "@powerhousedao/shared/document-drive";
|
|
209
|
+
import { useMemo } from "react";
|
|
210
|
+
|
|
211
|
+
function buildSidebarNodes(
|
|
212
|
+
nodes: Node[],
|
|
213
|
+
parentId: string | null | undefined,
|
|
214
|
+
): SidebarNode[] {
|
|
215
|
+
return nodes
|
|
216
|
+
.filter((n) => {
|
|
217
|
+
if (parentId == null) {
|
|
218
|
+
return n.parentFolder == null;
|
|
219
|
+
}
|
|
220
|
+
return n.parentFolder === parentId;
|
|
221
|
+
})
|
|
222
|
+
.map((node): SidebarNode => {
|
|
223
|
+
if (node.kind === "folder") {
|
|
224
|
+
return {
|
|
225
|
+
id: node.id,
|
|
226
|
+
title: node.name,
|
|
227
|
+
icon: "FolderClose" as const,
|
|
228
|
+
expandedIcon: "FolderOpen" as const,
|
|
229
|
+
children: buildSidebarNodes(nodes, node.id),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
id: node.id,
|
|
234
|
+
title: node.name,
|
|
235
|
+
icon: "File" as const,
|
|
236
|
+
};
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function transformNodesToSidebarNodes(
|
|
241
|
+
nodes: Node[],
|
|
242
|
+
driveName: string,
|
|
243
|
+
): SidebarNode[] {
|
|
244
|
+
return [
|
|
245
|
+
{
|
|
246
|
+
id: "root",
|
|
247
|
+
title: driveName,
|
|
248
|
+
icon: "Drive" as const,
|
|
249
|
+
children: buildSidebarNodes(nodes, null),
|
|
250
|
+
},
|
|
251
|
+
];
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Hierarchical folder tree navigation component using Sidebar from document-engineering.
|
|
256
|
+
* Displays folders and files in a tree structure with expand/collapse functionality, search, and resize support.
|
|
257
|
+
*/
|
|
258
|
+
export function FolderTree() {
|
|
259
|
+
const [selectedDrive] = useSelectedDrive();
|
|
260
|
+
const nodes = useNodesInSelectedDrive();
|
|
261
|
+
const selectedNode = useSelectedNode();
|
|
262
|
+
const driveName = selectedDrive.header.name;
|
|
263
|
+
// Transform Node[] to hierarchical SidebarNode structure
|
|
264
|
+
const sidebarNodes = useMemo(
|
|
265
|
+
() => transformNodesToSidebarNodes(nodes || [], driveName),
|
|
266
|
+
[nodes, driveName],
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
const handleActiveNodeChange = (node: SidebarNode) => {
|
|
270
|
+
// If root node is selected, pass undefined to match existing behavior
|
|
271
|
+
if (node.id === "root") {
|
|
272
|
+
setSelectedNode(undefined);
|
|
273
|
+
} else {
|
|
274
|
+
setSelectedNode(node.id);
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
// Map selectedNodeId to activeNodeId (use "root" when undefined)
|
|
278
|
+
const activeNodeId =
|
|
279
|
+
!selectedNode || selectedNode.id === selectedDrive.header.id
|
|
280
|
+
? "root"
|
|
281
|
+
: selectedNode.id;
|
|
282
|
+
|
|
283
|
+
return (
|
|
284
|
+
<SidebarProvider nodes={sidebarNodes}>
|
|
285
|
+
<Sidebar
|
|
286
|
+
className="pt-1"
|
|
287
|
+
nodes={sidebarNodes}
|
|
288
|
+
activeNodeId={activeNodeId}
|
|
289
|
+
onActiveNodeChange={handleActiveNodeChange}
|
|
290
|
+
sidebarTitle="Drive Explorer"
|
|
291
|
+
showSearchBar={true}
|
|
292
|
+
resizable={true}
|
|
293
|
+
allowPinning={false}
|
|
294
|
+
showStatusFilter={false}
|
|
295
|
+
initialWidth={256}
|
|
296
|
+
defaultLevel={2}
|
|
297
|
+
/>
|
|
298
|
+
</SidebarProvider>
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
`.raw;
|
|
302
|
+
//#endregion
|
|
303
|
+
//#region src/templates/app/components/NavigationBreadcrumbs.ts
|
|
304
|
+
const driveExplorerNavigationBreadcrumbsFileTemplate = () => tsx`
|
|
305
|
+
import { Breadcrumbs } from "@powerhousedao/design-system/connect";
|
|
306
|
+
|
|
307
|
+
/** Shows the navigation breadcrumbs for the selected drive or folder */
|
|
308
|
+
export function NavigationBreadcrumbs() {
|
|
309
|
+
return (
|
|
310
|
+
<div className="border-b border-gray-200 pb-3 space-y-3">
|
|
311
|
+
<Breadcrumbs />
|
|
312
|
+
</div>
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
`.raw;
|
|
316
|
+
//#endregion
|
|
317
|
+
//#region src/templates/app/config.ts
|
|
318
|
+
const appConfigFileTemplate = (v) => ts`
|
|
319
|
+
import type { PHAppConfig } from "@powerhousedao/reactor-browser";
|
|
320
|
+
|
|
321
|
+
/** Editor config for the <%= pascalCaseAppName %> */
|
|
322
|
+
export const editorConfig: PHAppConfig = {
|
|
323
|
+
isDragAndDropEnabled: ${v.isDragAndDropEnabledString},
|
|
324
|
+
allowedDocumentTypes: ${v.allowedDocumentTypesString}
|
|
325
|
+
};
|
|
326
|
+
`.raw;
|
|
327
|
+
//#endregion
|
|
328
|
+
//#region src/templates/app/editor.ts
|
|
329
|
+
const appEditorFileTemplate = () => tsx`
|
|
330
|
+
import { useSetPHAppConfig } from "@powerhousedao/reactor-browser";
|
|
331
|
+
import type { EditorProps } from "document-model";
|
|
332
|
+
import { DriveExplorer } from "./components/DriveExplorer.js";
|
|
333
|
+
import { editorConfig } from "./config.js";
|
|
334
|
+
|
|
335
|
+
/** Editor component for the app */
|
|
336
|
+
export default function Editor(props: EditorProps) {
|
|
337
|
+
// set the config for this app
|
|
338
|
+
// you can update these configs in \`./config.ts\`
|
|
339
|
+
useSetPHAppConfig(editorConfig);
|
|
340
|
+
return (
|
|
341
|
+
<div className="bg-gray-50 p-6">
|
|
342
|
+
<DriveExplorer {...props} />
|
|
343
|
+
</div>
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
`.raw;
|
|
347
|
+
//#endregion
|
|
348
|
+
//#region src/templates/boilerplate/AGENTS.md.ts
|
|
349
|
+
const agentsTemplate = md`
|
|
350
|
+
# Powerhouse Document Models Assistant
|
|
351
|
+
|
|
352
|
+
This project creates document models, editors, processors and subgraphs for the Powerhouse ecosystem. Your role is to help users create these modules based on their needs.
|
|
353
|
+
|
|
354
|
+
## Core Concepts
|
|
355
|
+
|
|
356
|
+
- **Document Model**: A template for creating documents. Defines schema and allowed operations for a document type.
|
|
357
|
+
- **Document**: An instance of a document model containing actual data that follows the model's structure and can be modified using operations.
|
|
358
|
+
- **Drive**: A document of type "powerhouse/document-drive" representing a collection of documents and folders. Add documents using "addActions" with "ADD_FILE" action.
|
|
359
|
+
- **Action**: A proposed change to a document (JSON object with action name and input). Dispatch using "addActions" tool.
|
|
360
|
+
- **Operation**: A completed change to a document containing the action plus metadata (index, timestamp, hash, errors). Actions become operations after dispatch.
|
|
361
|
+
|
|
362
|
+
## Technology Primer
|
|
363
|
+
|
|
364
|
+
- **Reactor**: The core Powerhouse engine. It is modular and storage-agnostic, loads document models at runtime, and synchronizes documents across nodes via drives.
|
|
365
|
+
- **Reactor Package**: A deployable bundle that extends the Reactor. It contains one or more document models, editors, processors, and subgraphs. A Vetra project generates a Reactor Package.
|
|
366
|
+
- **Connect**: The Powerhouse web application for document management. End users open Connect to browse drives, create documents, and interact with editors.
|
|
367
|
+
- **Switchboard**: The Powerhouse API service. It exposes GraphQL and MCP endpoints so external tools can read/write documents programmatically.
|
|
368
|
+
- **Vetra**: The local development environment for building Reactor Packages. It includes Vetra Studio (a local Connect instance) and Vetra Switchboard (a local Switchboard with reactor-mcp). Start it with \`ph vetra\`.
|
|
369
|
+
|
|
370
|
+
## CRITICAL: MCP Tool Usage Rules
|
|
371
|
+
|
|
372
|
+
**MANDATORY**: The \`reactor-mcp\` MUST BE USED when handling documents or document-models for the Powerhouse/Vetra ecosystem.
|
|
373
|
+
If the \`reactor-mcp\` server is unavailable, ask the user to run \`ph vetra\` on a separate terminal to start the server and try to reconnect to the MCP server, DO NOT run it yourself.
|
|
374
|
+
|
|
375
|
+
### Key Requirements:
|
|
376
|
+
|
|
377
|
+
- Never set document IDs manually - they're auto-generated by 'createDocument'
|
|
378
|
+
- Minimize "addActions" calls by batching multiple actions together
|
|
379
|
+
- Add new document model documents to "vetra-{hash}" drive unless specified otherwise
|
|
380
|
+
- Always check document model schema before calling addActions
|
|
381
|
+
- Use MCP tools for ALL document and document-model operations
|
|
382
|
+
|
|
383
|
+
## Document Model Creation Workflow
|
|
384
|
+
|
|
385
|
+
### 1. Planning Phase
|
|
386
|
+
|
|
387
|
+
**MANDATORY**: Present your proposal to the user and ask for confirmation before implementing ANY document model.
|
|
388
|
+
|
|
389
|
+
- **ALWAYS** describe the proposed document model structure (state schema, operations, modules) before creating
|
|
390
|
+
- **NEVER** proceed with implementation without explicit user approval of your proposal
|
|
391
|
+
- When in doubt, ask for clarification
|
|
392
|
+
- Break complex models into logical modules and operations
|
|
393
|
+
|
|
394
|
+
#### Document Type ID Format
|
|
395
|
+
|
|
396
|
+
- **Type ID**: \`{organization}/{document-type-name}\` (e.g., \`pizza-plaza/order\`, \`acme/invoice\`)
|
|
397
|
+
- **File extension**: 2-4 characters with leading dot (e.g., \`.ordr\`, \`.inv\`)
|
|
398
|
+
- **Name**: Must match \`/[a-zA-Z][a-zA-Z0-9 ]*/\` — human-readable, capitalized (e.g., \`"Order"\`, \`"Invoice"\`)
|
|
399
|
+
|
|
400
|
+
### 2. Pre-Implementation Requirements
|
|
401
|
+
|
|
402
|
+
**MANDATORY**: Check document model schema before making any MCP tool calls.
|
|
403
|
+
|
|
404
|
+
- **ALWAYS** use \`mcp__reactor-mcp__getDocumentModelSchema\` with \`type: "powerhouse/document-model"\` first
|
|
405
|
+
- Review input schema requirements for operations like \`ADD_MODULE\`, \`ADD_OPERATION\`, etc.
|
|
406
|
+
- Ensure all required parameters (like \`id\` or \`scope\` fields) are included in action inputs
|
|
407
|
+
- This prevents failed tool calls and reduces iteration
|
|
408
|
+
|
|
409
|
+
### 3. Implementation Requirements
|
|
410
|
+
|
|
411
|
+
- Document model reducers must be **pure synchronous functions**
|
|
412
|
+
- Reducers receive current state and operation, always returning the same result
|
|
413
|
+
- Values like dates/IDs must come from operation input, not generated in reducer
|
|
414
|
+
- Reducer code goes into SET_OPERATION_REDUCER action (no function header needed)
|
|
415
|
+
- Reducers are wrapped with Mutative - you can mutate the state object directly
|
|
416
|
+
- External imports go at the beginning of the actual reducer file in \`src/\`
|
|
417
|
+
- Ensure that the reducer code of each operation in the document model schema is applied in \`document-models/<document-model-name>/src/reducers/<module-name>.ts\`
|
|
418
|
+
|
|
419
|
+
### 4. Quality assurance
|
|
420
|
+
|
|
421
|
+
After doing changes to the code, or after creating a new document model or a new editor, _YOU MUST RUN_ the following commands to check for errors in your implementation:
|
|
422
|
+
|
|
423
|
+
- **TypeScript Check**: Run \`npm run tsc\` to validate type safety
|
|
424
|
+
- **ESLint Check**: Run \`npm run lint:fix\` to check for errors with ESLint
|
|
425
|
+
|
|
426
|
+
## Document editor creation flow
|
|
427
|
+
|
|
428
|
+
**CRITICAL**: Creating a document editor is a **two-phase** process. You must NEVER skip Phase 1 or try to manually create editor files from scratch. The codegen system generates the boilerplate — your job is only to implement the UI inside it.
|
|
429
|
+
|
|
430
|
+
### Phase 1: Create the editor document via MCP (MANDATORY FIRST STEP)
|
|
431
|
+
|
|
432
|
+
**NEVER** start by writing editor code, creating component files, or looking at how to scaffold an editor manually. The **only** way to create a new editor is through the MCP tools:
|
|
433
|
+
|
|
434
|
+
1. Check if the document editor already exists. If it does, ask the user if a new one should be created or if the existing one should be reimplemented
|
|
435
|
+
2. If it's a new editor, get the document editor schema using \`mcp__reactor-mcp__getDocumentModelSchema\` with \`type: "powerhouse/document-editor"\`
|
|
436
|
+
3. Create a new editor document on the \`vetra-{hash}\` drive of type \`powerhouse/document-editor\` using \`mcp__reactor-mcp__addActions\` with the \`ADD_FILE\` action
|
|
437
|
+
4. Configure the editor document with the required actions (set the editor name, target document model, etc.) according to the schema
|
|
438
|
+
|
|
439
|
+
⚠️ **The editor document MUST be confirmed/published — if it is left as draft state, the codegen will NOT run and no editor files will be generated.** Make sure the document state is not "DRAFT" after creation.
|
|
440
|
+
|
|
441
|
+
5. Once the editor document is confirmed on the drive, the codegen automatically runs and generates boilerplate files in the \`editors/\` folder, including hooks, type definitions, and the editor component shell
|
|
442
|
+
|
|
443
|
+
### Phase 2: Implement the editor UI
|
|
444
|
+
|
|
445
|
+
Only **after** the codegen has produced the boilerplate files, proceed with the UI implementation:
|
|
446
|
+
|
|
447
|
+
- Inspect the generated files in the \`editors/\` folder — do NOT create new files for the main editor component; edit the generated one
|
|
448
|
+
- Inspect the hooks in \`editors/hooks\` as they should be useful
|
|
449
|
+
- Read the schema of the document model that the editor is for to know how to interact with it
|
|
450
|
+
- Every editor **MUST** include \`<DocumentToolbar />\` imported from \`@powerhousedao/design-system/connect/index\`. Place it at the top of the editor component — do not put anything next to it.
|
|
451
|
+
- Style the editor using tailwind classes or a style tag. If using a style tag, make sure to make the selectors specific to only apply to the editor component.
|
|
452
|
+
- Create modular components for the UI elements and place them on separate files to make it easier to maintain and update
|
|
453
|
+
- Consider using the React Components exported by \`@powerhousedao/design-system\` and \`@powerhousedao/document-engineering\`
|
|
454
|
+
- Separate business logic from presentation logic
|
|
455
|
+
- Use TypeScript for type safety, avoid using any and type casting
|
|
456
|
+
- Always check for type and lint errors after creating or modifying the editor
|
|
457
|
+
- **CRITICAL**: After creating a new editor, verify that \`editors/editors.ts\` includes the new editor module. The codegen should update this file automatically, but if it doesn't, manually add the import and include the editor in the \`editors\` array. Without this registration, Connect won't find an editor for the document type. Example:
|
|
458
|
+
|
|
459
|
+
~~~typescript
|
|
460
|
+
import type { EditorModule } from "document-model";
|
|
461
|
+
import { TodoListEditor } from "./todo-list-editor/module.js";
|
|
462
|
+
|
|
463
|
+
export const editors: EditorModule[] = [TodoListEditor];
|
|
464
|
+
~~~
|
|
465
|
+
|
|
466
|
+
### Document Editor Implementation Pattern
|
|
467
|
+
|
|
468
|
+
**CRITICAL**: When implementing document editors, use the modern React hooks pattern from \`@powerhousedao/reactor-browser\`.
|
|
469
|
+
|
|
470
|
+
The following section is valid for editors that edit a single document type.
|
|
471
|
+
|
|
472
|
+
#### Required Imports and Setup
|
|
473
|
+
|
|
474
|
+
Using a "Todo" document model as example:
|
|
475
|
+
|
|
476
|
+
~~~typescript
|
|
477
|
+
import { generateId } from "document-model";
|
|
478
|
+
import { useSelectedTodoDocument } from "../hooks/useTodoDocument.js";
|
|
479
|
+
import {
|
|
480
|
+
addTodo,
|
|
481
|
+
} from "../../document-models/todo/gen/creators.js";
|
|
482
|
+
|
|
483
|
+
export default function Editor() {
|
|
484
|
+
const [document, dispatch] = useSelectedTodoDocument();
|
|
485
|
+
|
|
486
|
+
function handleAddTodo(values: { title: string }) {
|
|
487
|
+
if (values.title) {
|
|
488
|
+
dispatch(addTodo({ id: generateId(), title: values.title }));
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
// Note: The \`useSelectedTodoDocument\` hook is auto-generated. Check the \`editors/hooks\` folder for the exact hook name.
|
|
493
|
+
// Action creators like \`addTodo\` are exported from the document model's \`gen/creators.js\` file.
|
|
494
|
+
~~~
|
|
495
|
+
|
|
496
|
+
The \`useSelectedTodoDocument\` gets generated automatically so you don't need to implement it yourself.
|
|
497
|
+
|
|
498
|
+
## ⚠️ CRITICAL: Generated Files & Modification Rules
|
|
499
|
+
|
|
500
|
+
### Generated Files Rule
|
|
501
|
+
|
|
502
|
+
**NEVER edit files in \`gen/\` folders** - they are auto-generated and will be overwritten.
|
|
503
|
+
|
|
504
|
+
### Document Model Modification Process
|
|
505
|
+
|
|
506
|
+
For ANY document model changes, follow this **mandatory** two-step process:
|
|
507
|
+
|
|
508
|
+
#### Step 1: Update Document Model via MCP
|
|
509
|
+
|
|
510
|
+
Use \`mcp__reactor-mcp__addActions\` with operations like:
|
|
511
|
+
|
|
512
|
+
- \`SET_OPERATION_SCHEMA\` - update input/output schemas
|
|
513
|
+
- \`SET_OPERATION_REDUCER\` - update reducer code
|
|
514
|
+
- \`SET_STATE_SCHEMA\` - update state definitions
|
|
515
|
+
|
|
516
|
+
#### Step 2: Update Existing Source Files
|
|
517
|
+
|
|
518
|
+
**ALSO manually update existing reducer files in \`src/\` folder** - these are NOT auto-generated.
|
|
519
|
+
Make sure to check if the operation reducer code needs to be updated after changing the state schema.
|
|
520
|
+
|
|
521
|
+
### ⚠️ Critical Reminder
|
|
522
|
+
|
|
523
|
+
**ALWAYS do BOTH steps when fixing reducer issues:**
|
|
524
|
+
|
|
525
|
+
1. ✅ Fix existing reducer files in \`src/\` manually
|
|
526
|
+
2. ✅ Update document model via MCP with same fixes
|
|
527
|
+
|
|
528
|
+
**Forgetting step 2 means future code generations will still contain the bugs!**
|
|
529
|
+
|
|
530
|
+
## Reducer Implementation Guidelines
|
|
531
|
+
|
|
532
|
+
### ❌ Forbidden in Reducers (Non-Deterministic)
|
|
533
|
+
|
|
534
|
+
- \`crypto.randomUUID()\`, \`Math.random()\`, \`Date.now()\`, \`new Date()\`
|
|
535
|
+
- External API calls or side effects
|
|
536
|
+
- Asynchronous functions
|
|
537
|
+
- Any non-deterministic functions
|
|
538
|
+
|
|
539
|
+
### ❌ Forbidden Patterns
|
|
540
|
+
|
|
541
|
+
~~~typescript
|
|
542
|
+
// NEVER use fallback values with non-deterministic functions
|
|
543
|
+
id: action.input.id || crypto.randomUUID(); // ❌ FORBIDDEN
|
|
544
|
+
timestamp: action.input.timestamp || new Date(); // ❌ FORBIDDEN
|
|
545
|
+
~~~
|
|
546
|
+
|
|
547
|
+
### ✅ Required Pattern
|
|
548
|
+
|
|
549
|
+
All dynamic values must come from action input:
|
|
550
|
+
|
|
551
|
+
- **IDs**: Include \`id: OID!\` in input schema, use \`action.input.id\` in reducer
|
|
552
|
+
- **Timestamps**: Include \`timestamp: DateTime!\` in input schema
|
|
553
|
+
- **Computed values**: Calculate before dispatching action
|
|
554
|
+
|
|
555
|
+
### Example
|
|
556
|
+
|
|
557
|
+
~~~typescript
|
|
558
|
+
// ❌ BAD - impure reducer
|
|
559
|
+
const newItem = {
|
|
560
|
+
id: crypto.randomUUID(), // Non-deterministic
|
|
561
|
+
createdAt: new Date(), // Non-deterministic
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
// ✅ GOOD - pure reducer
|
|
565
|
+
const newItem = {
|
|
566
|
+
id: action.input.id, // From action input
|
|
567
|
+
createdAt: action.input.createdAt, // From action input
|
|
568
|
+
};
|
|
569
|
+
~~~
|
|
570
|
+
|
|
571
|
+
### Handling Nullable Input Types
|
|
572
|
+
|
|
573
|
+
**CRITICAL**: Be careful when handling optional input types:
|
|
574
|
+
|
|
575
|
+
- Optional input types use \`InputMaybe<T>\` allowing \`null | undefined | T\`.
|
|
576
|
+
- Optional state types use \`Maybe<T>\` = \`T | null\`.
|
|
577
|
+
- If there is no applicable default value then use \`|| null\`.
|
|
578
|
+
|
|
579
|
+
~~~typescript
|
|
580
|
+
// ❌ BAD - Type error with Maybe<string>
|
|
581
|
+
amount: action.input.amount,
|
|
582
|
+
notes: action.input.notes,
|
|
583
|
+
|
|
584
|
+
// ✅ GOOD - Matches Maybe<T> = T | null
|
|
585
|
+
amount: action.input.amount || null,
|
|
586
|
+
notes: action.input.notes || [],
|
|
587
|
+
~~~
|
|
588
|
+
|
|
589
|
+
Use truthy checks when conditionally assigning optional values from input to state:
|
|
590
|
+
|
|
591
|
+
~~~typescript
|
|
592
|
+
// ❌ BAD - Type 'string | null' is not assignable to type 'string'.
|
|
593
|
+
if (action.input.field !== undefined) entry.field = action.input.field;
|
|
594
|
+
|
|
595
|
+
// ✅ GOOD - use truthy checks
|
|
596
|
+
if (action.input.field) state.field = action.input.field;
|
|
597
|
+
|
|
598
|
+
// ✅ GOOD - For booleans use explicit null/undefined checks
|
|
599
|
+
if (action.input.field !== undefined && action.input.field !== null)
|
|
600
|
+
state.field = action.input.field;
|
|
601
|
+
~~~
|
|
602
|
+
|
|
603
|
+
### Error Handling in Operations
|
|
604
|
+
|
|
605
|
+
**MANDATORY**: Define specific error types for each operation to handle invalid inputs and edge cases properly.
|
|
606
|
+
Action inputs are validated so they are guaranteed to respect the input schema.
|
|
607
|
+
Errors referenced in the reducer code will be imported automatically.
|
|
608
|
+
|
|
609
|
+
#### Error Definition Requirements
|
|
610
|
+
|
|
611
|
+
1. **Add error definitions** to operations using \`ADD_OPERATION_ERROR\`:
|
|
612
|
+
- \`errorCode\`: Uppercase snake_case (e.g., \`"MISSING_ID"\`, \`"ENTRY_NOT_FOUND"\`)
|
|
613
|
+
- \`errorName\`: PascalCase ending with "Error" (e.g., \`"MissingIdError"\`, \`"EntryNotFoundError"\`)
|
|
614
|
+
- \`errorDescription\`: Human-readable description of the error condition
|
|
615
|
+
|
|
616
|
+
2. **Error names must end with "Error"** for consistency and code generation
|
|
617
|
+
|
|
618
|
+
3. **Use specific error types** rather than generic validation
|
|
619
|
+
|
|
620
|
+
4. **Must use unique error names and ids**
|
|
621
|
+
|
|
622
|
+
#### Error Usage in Reducers
|
|
623
|
+
|
|
624
|
+
~~~typescript
|
|
625
|
+
// ✅ GOOD - Throw specific errors by name
|
|
626
|
+
if (!action.input.id) {
|
|
627
|
+
throw new MissingIdError("ID is required for operation");
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
if (entryIndex === -1) {
|
|
631
|
+
throw new EntryNotFoundError(\`Entry not found\`);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// ❌ BAD - Generic Error
|
|
635
|
+
throw new Error("Something went wrong");
|
|
636
|
+
|
|
637
|
+
// ❌ BAD - Nested error access
|
|
638
|
+
throw new errors.ModuleName.MissingIdError("message");
|
|
639
|
+
|
|
640
|
+
// ❌ BAD - Do not import error classes in the reducer code,
|
|
641
|
+
import { MissingIdError } from "../../gen/module-name/error.js";
|
|
642
|
+
|
|
643
|
+
// ✅ GOOD - Simply reference the error and it will be imported automatically
|
|
644
|
+
throw new MissingIdError("message");
|
|
645
|
+
~~~
|
|
646
|
+
|
|
647
|
+
#### Common Error Patterns
|
|
648
|
+
|
|
649
|
+
- **EntityNotFoundError**: Referenced entity doesn't exist
|
|
650
|
+
- **DuplicateIdError**: ID already exists when creating new entries
|
|
651
|
+
- **InvalidInputError**: Business logic violations
|
|
652
|
+
- **PermissionDeniedError**: Access control violations
|
|
653
|
+
|
|
654
|
+
#### Testing Reducer Errors
|
|
655
|
+
|
|
656
|
+
**CRITICAL**: When a reducer throws an error, the operation is **still added** to the document but with an \`.error\` property containing the error message as a string.
|
|
657
|
+
|
|
658
|
+
**DO NOT** use \`.toThrow()\` or \`expect(() => ...).toThrow()\` patterns when testing reducer errors.
|
|
659
|
+
|
|
660
|
+
##### How Errors Work in Operations
|
|
661
|
+
|
|
662
|
+
1. The reducer throws an error (e.g., \`throw new InvalidNameError("message")\`)
|
|
663
|
+
2. The operation is still recorded in \`document.operations.global\` (or \`.local\`)
|
|
664
|
+
3. The error message is stored in \`operation.error\` as a string
|
|
665
|
+
4. **The state is NOT mutated** - it remains unchanged from before the operation
|
|
666
|
+
|
|
667
|
+
##### Accessing the Correct Operation Index
|
|
668
|
+
|
|
669
|
+
**CRITICAL**: You must access the correct operation index. The index corresponds to how many operations were dispatched before it.
|
|
670
|
+
|
|
671
|
+
- If this is the **first** operation dispatched, access \`[0]\`
|
|
672
|
+
- If 3 operations were dispatched **before** the failing one, access \`[3]\`
|
|
673
|
+
|
|
674
|
+
##### Example
|
|
675
|
+
|
|
676
|
+
~~~typescript
|
|
677
|
+
it("should return error and not mutate state", () => {
|
|
678
|
+
const document = utils.createDocument();
|
|
679
|
+
const initialState = document.state.global.name;
|
|
680
|
+
|
|
681
|
+
const updatedDocument = reducer(document, setName({ name: "invalid" }));
|
|
682
|
+
|
|
683
|
+
// Access the correct operation index (0 = first operation)
|
|
684
|
+
expect(updatedDocument.operations.global[0].error).toBe(
|
|
685
|
+
"Name is not allowed",
|
|
686
|
+
);
|
|
687
|
+
// State remains unchanged
|
|
688
|
+
expect(updatedDocument.state.global.name).toBe(initialState);
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
// ❌ WRONG - Never use toThrow()
|
|
692
|
+
expect(() => reducer(document, setName({ name: "invalid" }))).toThrow();
|
|
693
|
+
~~~
|
|
694
|
+
|
|
695
|
+
## Document Model Structure
|
|
696
|
+
|
|
697
|
+
### Core Components
|
|
698
|
+
|
|
699
|
+
- **Basic Metadata**: \`id\`, \`name\`, \`extension\`, \`description\`, \`author\` (name + website)
|
|
700
|
+
- **Specifications**: Versioned specs with \`version\`, \`changeLog\`, \`state\` (global/local with schema, initialValue, examples)
|
|
701
|
+
- **Modules**: Operational modules containing their operations
|
|
702
|
+
|
|
703
|
+
## Available Document Model Operations (37 total)
|
|
704
|
+
|
|
705
|
+
| Category | Operations | Count |
|
|
706
|
+
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
|
|
707
|
+
| **Header Management** | \`SET_MODEL_NAME\`, \`SET_MODEL_ID\`, \`SET_MODEL_EXTENSION\`, \`SET_MODEL_DESCRIPTION\`, \`SET_AUTHOR_NAME\`, \`SET_AUTHOR_WEBSITE\` | 6 |
|
|
708
|
+
| **Versioning** | ⚠️ **DO NOT USE** - Not implemented | 0 |
|
|
709
|
+
| **Module Management** | \`ADD_MODULE\`, \`SET_MODULE_NAME\`, \`SET_MODULE_DESCRIPTION\`, \`DELETE_MODULE\`, \`REORDER_MODULES\` | 5 |
|
|
710
|
+
| **Operation Management** | \`ADD_OPERATION\`, \`SET_OPERATION_NAME\`, \`SET_OPERATION_SCHEMA\`, \`SET_OPERATION_DESCRIPTION\`, \`SET_OPERATION_TEMPLATE\`, \`SET_OPERATION_REDUCER\`, \`MOVE_OPERATION\`, \`DELETE_OPERATION\`, \`REORDER_MODULE_OPERATIONS\` | 9 |
|
|
711
|
+
| **Operation Error Management** | \`ADD_OPERATION_ERROR\`, \`SET_OPERATION_ERROR_CODE\`, \`SET_OPERATION_ERROR_NAME\`, \`SET_OPERATION_ERROR_DESCRIPTION\`, \`SET_OPERATION_ERROR_TEMPLATE\`, \`DELETE_OPERATION_ERROR\`, \`REORDER_OPERATION_ERRORS\` | 7 |
|
|
712
|
+
| **Operation Example Management** | \`ADD_OPERATION_EXAMPLE\`, \`UPDATE_OPERATION_EXAMPLE\`, \`DELETE_OPERATION_EXAMPLE\`, \`REORDER_OPERATION_EXAMPLES\` | 4 |
|
|
713
|
+
| **State Management** | \`SET_STATE_SCHEMA\`, \`SET_INITIAL_STATE\`, \`ADD_STATE_EXAMPLE\`, \`UPDATE_STATE_EXAMPLE\`, \`DELETE_STATE_EXAMPLE\`, \`REORDER_STATE_EXAMPLES\` | 6 |
|
|
714
|
+
|
|
715
|
+
## Best Practices & Design Principles
|
|
716
|
+
|
|
717
|
+
### Scope Selection
|
|
718
|
+
|
|
719
|
+
- **\`scope: "global"\`**: State shared among all users with document access
|
|
720
|
+
- **\`scope: "local"\`**: State private to each individual user
|
|
721
|
+
|
|
722
|
+
### Operation Design
|
|
723
|
+
|
|
724
|
+
- Use descriptive operation names (e.g., \`ADD_LINE_ITEM\`, \`UPDATE_RECIPIENT\`)
|
|
725
|
+
- One operation per user intent (separate concerns)
|
|
726
|
+
- Include comprehensive examples and error definitions
|
|
727
|
+
- Organize related operations into logical modules
|
|
728
|
+
|
|
729
|
+
## GraphQL Schema Guidelines
|
|
730
|
+
|
|
731
|
+
### Document State Schema
|
|
732
|
+
|
|
733
|
+
- **Most fields optional** to support creating empty documents
|
|
734
|
+
- Use required fields \`!\` only when absolutely necessary
|
|
735
|
+
- Defaults handled by operations, not schema
|
|
736
|
+
|
|
737
|
+
#### Mandatory vs Optional Field Rules
|
|
738
|
+
|
|
739
|
+
A user must always be able to create an **empty document** without providing any information. This drives the following rules:
|
|
740
|
+
|
|
741
|
+
- **Root type properties** can only be mandatory (\`!\`) if they have a logical default value (e.g., empty array, enum initial status)
|
|
742
|
+
- **Collections** should always use \`[Type!]!\` — inner \`!\` means no nulls in the array, outer \`!\` means the array itself defaults to empty
|
|
743
|
+
- **Child object fields** can be mandatory only if all their required properties also have logical defaults
|
|
744
|
+
- Use \`enum\` types for workflow statuses (e.g., \`status: OrderStatus!\` where the enum has an initial value like \`DRAFT\`)
|
|
745
|
+
|
|
746
|
+
### ⚠️ CRITICAL: State Type Naming Convention
|
|
747
|
+
|
|
748
|
+
**MANDATORY**: The global state type name MUST follow this exact pattern:
|
|
749
|
+
|
|
750
|
+
~~~graphql
|
|
751
|
+
type <DocumentModelName>State {
|
|
752
|
+
# your fields here
|
|
753
|
+
}
|
|
754
|
+
~~~
|
|
755
|
+
|
|
756
|
+
**DO NOT** append "Global" to the state type name, even when defining global state:
|
|
757
|
+
|
|
758
|
+
~~~graphql
|
|
759
|
+
// ❌ WRONG - Do not use "GlobalState" suffix
|
|
760
|
+
type TodoListGlobalState {
|
|
761
|
+
todos: [Todo!]!
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// ✅ CORRECT - Use only "State" suffix
|
|
765
|
+
type TodoListState {
|
|
766
|
+
todos: [Todo!]!
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// ✅ CORRECT - Use "LocalState" suffix for Local scope
|
|
770
|
+
type TodoListLocalState {
|
|
771
|
+
localTodos: [Todo!]!
|
|
772
|
+
}
|
|
773
|
+
~~~
|
|
774
|
+
|
|
775
|
+
**Why this matters:**
|
|
776
|
+
|
|
777
|
+
- The code generator expects the type to be named \`<DocumentModelName>State\`
|
|
778
|
+
- Using \`GlobalState\` or \`LocalState\` suffix will cause TypeScript compilation errors
|
|
779
|
+
- This applies when using \`SET_STATE_SCHEMA\` with \`scope: "global"\`
|
|
780
|
+
|
|
781
|
+
**Rule**: For global state, the type should be \`<DocumentModelName>State\`. For local state (if needed), the type name should be \`<DocumentModelName>LocalState\`.
|
|
782
|
+
|
|
783
|
+
### Available Scalar Types
|
|
784
|
+
|
|
785
|
+
| Standard | Custom Identity | Custom Amounts | Custom Specialized |
|
|
786
|
+
| --------- | ---------------------- | ------------------- | ------------------ |
|
|
787
|
+
| \`String\` | \`OID\` (Object ID) | \`Amount\` | \`EthereumAddress\` |
|
|
788
|
+
| \`Int\` | \`PHID\` (Powerhouse ID) | \`Amount_Tokens\` | \`EmailAddress\` |
|
|
789
|
+
| \`Float\` | \`OLabel\` | \`Amount_Money\` | \`Date\` |
|
|
790
|
+
| \`Boolean\` | | \`Amount_Fiat\` | \`DateTime\` |
|
|
791
|
+
| | | \`Amount_Currency\` | \`URL\` |
|
|
792
|
+
| | | \`Amount_Crypto\` | \`Currency\` |
|
|
793
|
+
| | | \`Amount_Percentage\` | |
|
|
794
|
+
|
|
795
|
+
### Arrays and Objects
|
|
796
|
+
|
|
797
|
+
- **Arrays**: Must be mandatory \`[ObjectType!]!\`
|
|
798
|
+
- **Objects in arrays**: Must include \`OID!\` field for unique identification
|
|
799
|
+
- Include \`OLabel\` for metadata when relevant
|
|
800
|
+
|
|
801
|
+
#### OID vs PHID Usage
|
|
802
|
+
|
|
803
|
+
- \`OID\` is used as **primary key** (\`id: OID!\`) and **foreign key** (\`otherObjectId: OID!\`) within a document
|
|
804
|
+
- \`PHID\` is **only** for referencing **external documents** (other documents in the drive), typically alongside cached properties (like a link preview — title/snippet may become stale)
|
|
805
|
+
- **NEVER** use the \`ID\` type — it is a common GraphQL convention but is not used in Powerhouse document models
|
|
806
|
+
|
|
807
|
+
#### Collection Sorting & Trees
|
|
808
|
+
|
|
809
|
+
- **No need for \`position\` or \`weight\` properties** — maintain order via array index; operations like \`MOVE_X\` reorder the array directly
|
|
810
|
+
- **Trees**: Always define as a flat list with \`parentId: OID\` (root nodes have \`parentId = null\`); do NOT use recursive/nested types
|
|
811
|
+
|
|
812
|
+
### Input Types
|
|
813
|
+
|
|
814
|
+
- Reflect user intent with descriptive names
|
|
815
|
+
- Simple, specific fields over complex nested types
|
|
816
|
+
- System auto-generates \`OID\` for new objects (users don't provide manually)
|
|
817
|
+
|
|
818
|
+
#### Input Type Naming Convention
|
|
819
|
+
|
|
820
|
+
- Root input type **MUST** be named \`<OperationName>Input\` (PascalCase of the operation name)
|
|
821
|
+
- Example: operation \`SET_CATEGORY_LABEL\` → input type \`SetCategoryLabelInput\`
|
|
822
|
+
- **Failing to follow this convention breaks the code generator**
|
|
823
|
+
|
|
824
|
+
#### Input Types Cannot Reference State Types
|
|
825
|
+
|
|
826
|
+
- In operation input schemas, **ONLY** \`enum\` types and scalar types from the state schema can be referenced directly
|
|
827
|
+
- All other state types must be **mirrored** with unique input types (e.g., state type \`MenuItem\` → input type \`NewMenuItemInput\` for the ADD operation)
|
|
828
|
+
- State \`enum\` types **MUST NOT** be redefined in input schemas — reference them directly
|
|
829
|
+
- Each operation should have its **own** input types; do not share mirror types across operations
|
|
830
|
+
|
|
831
|
+
#### Empty Input Workaround
|
|
832
|
+
|
|
833
|
+
- Input types with **zero fields** are not supported by the code generator
|
|
834
|
+
- Workaround: add \`_: Boolean\` as a dummy optional parameter
|
|
835
|
+
|
|
836
|
+
~~~graphql
|
|
837
|
+
# ❌ BAD - empty input type breaks codegen
|
|
838
|
+
input ClearAllInput {}
|
|
839
|
+
|
|
840
|
+
# ✅ GOOD - dummy field workaround
|
|
841
|
+
input ClearAllInput {
|
|
842
|
+
_: Boolean
|
|
843
|
+
}
|
|
844
|
+
~~~
|
|
845
|
+
|
|
846
|
+
## Working with Drives
|
|
847
|
+
|
|
848
|
+
**MANDATORY**: Check the document-drive schema before performing drive operations.
|
|
849
|
+
|
|
850
|
+
### Drive Types
|
|
851
|
+
|
|
852
|
+
There might be two drives available with a special use case:
|
|
853
|
+
|
|
854
|
+
1. **Vetra Drive** (\`vetra-{hash}\`):
|
|
855
|
+
- Contains **source documents**: document models and document editors
|
|
856
|
+
- Used for development
|
|
857
|
+
- Add document model and editor definitions here
|
|
858
|
+
|
|
859
|
+
2. **Preview Drive** (\`preview-{hash}\`, named "Vetra Preview"):
|
|
860
|
+
- Contains **demo and preview documents** (document instances)
|
|
861
|
+
- Used for showcasing and testing document models
|
|
862
|
+
- Add actual document instances here
|
|
863
|
+
|
|
864
|
+
### Drive Operations
|
|
865
|
+
|
|
866
|
+
When working with drives (adding/removing documents, creating folders, etc.):
|
|
867
|
+
|
|
868
|
+
1. **Always get the drive schema first**:
|
|
869
|
+
|
|
870
|
+
~~~typescript
|
|
871
|
+
mcp__reactor -
|
|
872
|
+
mcp__getDocumentModelSchema({ type: "powerhouse/document-drive" });
|
|
873
|
+
~~~
|
|
874
|
+
|
|
875
|
+
2. **Review available operations** in the schema, such as:
|
|
876
|
+
- \`ADD_FILE\` - Add a document to the drive
|
|
877
|
+
- \`ADD_FOLDER\` - Create a new folder
|
|
878
|
+
- \`DELETE_NODE\` - Remove a file or folder (use this, NOT "DELETE_FILE")
|
|
879
|
+
- \`UPDATE_NODE\` - Update node properties
|
|
880
|
+
- \`MOVE_NODE\` - Move a node to different location
|
|
881
|
+
|
|
882
|
+
3. **Check input schemas** for each operation to ensure you're passing correct parameters
|
|
883
|
+
`.raw;
|
|
884
|
+
//#endregion
|
|
885
|
+
//#region src/templates/boilerplate/claude/settings.local.json.ts
|
|
886
|
+
const claudeSettingsLocalTemplate = json`
|
|
887
|
+
{
|
|
888
|
+
"$schema": "https://json.schemastore.org/claude-code-settings.json",
|
|
889
|
+
"permissions": {
|
|
890
|
+
"allow": ["Bash(npm run tsc:*)", "Bash(npm run lint:*)"],
|
|
891
|
+
"deny": [
|
|
892
|
+
"Write(./document-models/*/gen/**)",
|
|
893
|
+
"Write(./.ph/**)",
|
|
894
|
+
"Edit(./document-models/*/gen/**)",
|
|
895
|
+
"Edit(./.ph/**)"
|
|
896
|
+
]
|
|
897
|
+
},
|
|
898
|
+
"enableAllProjectMcpServers": true,
|
|
899
|
+
"enabledMcpjsonServers": ["reactor-mcp"]
|
|
900
|
+
}
|
|
901
|
+
`.raw;
|
|
902
|
+
//#endregion
|
|
903
|
+
//#region src/templates/boilerplate/cursor/mcp.json.ts
|
|
904
|
+
const cursorMcpTemplate = json`
|
|
905
|
+
{
|
|
906
|
+
"mcpServers": {
|
|
907
|
+
"reactor-mcp": {
|
|
908
|
+
"type": "http",
|
|
909
|
+
"url": "http://localhost:4001/mcp"
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
`.raw;
|
|
914
|
+
//#endregion
|
|
915
|
+
//#region src/templates/boilerplate/docker/connect-entrypoint.sh.ts
|
|
916
|
+
const connectEntrypointTemplate = `#!/bin/sh
|
|
917
|
+
set -e
|
|
918
|
+
|
|
919
|
+
# Substitute environment variables in nginx configuration
|
|
920
|
+
envsubst '\${PORT},\${PH_CONNECT_BASE_PATH}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf
|
|
921
|
+
|
|
922
|
+
echo "Testing nginx configuration..."
|
|
923
|
+
nginx -t
|
|
924
|
+
|
|
925
|
+
if [ $? -eq 0 ]; then
|
|
926
|
+
echo "Connect available at: http://localhost:\${PORT}\${PH_CONNECT_BASE_PATH}"
|
|
927
|
+
exec nginx -g "daemon off;"
|
|
928
|
+
else
|
|
929
|
+
echo "Nginx configuration test failed"
|
|
930
|
+
exit 1
|
|
931
|
+
fi
|
|
932
|
+
`;
|
|
933
|
+
//#endregion
|
|
934
|
+
//#region src/templates/boilerplate/docker/Dockerfile.ts
|
|
935
|
+
const dockerfileTemplate = `# =============================================================================
|
|
936
|
+
# Multi-stage Dockerfile for Powerhouse Document Model Packages
|
|
937
|
+
# Produces two images: connect (frontend) and switchboard (backend)
|
|
938
|
+
#
|
|
939
|
+
# Build commands:
|
|
940
|
+
# docker build --target connect -t <registry>/<project>/connect:<tag> .
|
|
941
|
+
# docker build --target switchboard -t <registry>/<project>/switchboard:<tag> .
|
|
942
|
+
# =============================================================================
|
|
943
|
+
|
|
944
|
+
# -----------------------------------------------------------------------------
|
|
945
|
+
# Base stage: Common setup for building
|
|
946
|
+
# -----------------------------------------------------------------------------
|
|
947
|
+
FROM node:24-alpine AS base
|
|
948
|
+
|
|
949
|
+
WORKDIR /app
|
|
950
|
+
|
|
951
|
+
# Install build dependencies
|
|
952
|
+
RUN apk add --no-cache python3 make g++ git bash \\
|
|
953
|
+
&& ln -sf /usr/bin/python3 /usr/bin/python
|
|
954
|
+
|
|
955
|
+
# Setup pnpm
|
|
956
|
+
ENV PNPM_HOME="/pnpm"
|
|
957
|
+
ENV PATH="$PNPM_HOME:$PATH"
|
|
958
|
+
RUN corepack enable && corepack prepare pnpm@latest --activate
|
|
959
|
+
|
|
960
|
+
# Configure JSR registry
|
|
961
|
+
RUN pnpm config set @jsr:registry https://npm.jsr.io
|
|
962
|
+
|
|
963
|
+
# Build arguments
|
|
964
|
+
ARG TAG=latest
|
|
965
|
+
ARG PH_CONNECT_BASE_PATH="/"
|
|
966
|
+
|
|
967
|
+
# Install ph-cmd, prisma, and prettier globally
|
|
968
|
+
RUN pnpm add -g ph-cmd@$TAG prisma@5.17.0 prettier
|
|
969
|
+
|
|
970
|
+
# Initialize project based on tag (dev/staging/latest)
|
|
971
|
+
RUN case "$TAG" in \\
|
|
972
|
+
*dev*) ph init project --dev --package-manager pnpm ;; \\
|
|
973
|
+
*staging*) ph init project --staging --package-manager pnpm ;; \\
|
|
974
|
+
*) ph init project --package-manager pnpm ;; \\
|
|
975
|
+
esac
|
|
976
|
+
|
|
977
|
+
WORKDIR /app/project
|
|
978
|
+
|
|
979
|
+
# Copy package files for the current package
|
|
980
|
+
COPY package.json pnpm-lock.yaml ./
|
|
981
|
+
|
|
982
|
+
# Install the current package (this package)
|
|
983
|
+
ARG PACKAGE_NAME
|
|
984
|
+
RUN if [ -n "$PACKAGE_NAME" ]; then \\
|
|
985
|
+
echo "Installing package: $PACKAGE_NAME"; \\
|
|
986
|
+
ph install "$PACKAGE_NAME"; \\
|
|
987
|
+
else \\
|
|
988
|
+
echo "Warning: PACKAGE_NAME not provided, using local build"; \\
|
|
989
|
+
pnpm install; \\
|
|
990
|
+
fi
|
|
991
|
+
|
|
992
|
+
# Regenerate Prisma client for Alpine Linux
|
|
993
|
+
RUN prisma generate --schema node_modules/document-drive/dist/prisma/schema.prisma || true
|
|
994
|
+
|
|
995
|
+
# -----------------------------------------------------------------------------
|
|
996
|
+
# Connect build stage
|
|
997
|
+
# -----------------------------------------------------------------------------
|
|
998
|
+
FROM base AS connect-builder
|
|
999
|
+
|
|
1000
|
+
ARG PH_CONNECT_BASE_PATH="/"
|
|
1001
|
+
|
|
1002
|
+
# Build connect
|
|
1003
|
+
RUN ph connect build --base \${PH_CONNECT_BASE_PATH}
|
|
1004
|
+
|
|
1005
|
+
# -----------------------------------------------------------------------------
|
|
1006
|
+
# Connect final stage - nginx
|
|
1007
|
+
# -----------------------------------------------------------------------------
|
|
1008
|
+
FROM nginx:alpine AS connect
|
|
1009
|
+
|
|
1010
|
+
# Install envsubst for config templating
|
|
1011
|
+
RUN apk add --no-cache gettext
|
|
1012
|
+
|
|
1013
|
+
# Copy nginx config template
|
|
1014
|
+
COPY docker/nginx.conf /etc/nginx/nginx.conf.template
|
|
1015
|
+
|
|
1016
|
+
# Copy built static files from build stage
|
|
1017
|
+
COPY --from=connect-builder /app/project/.ph/connect-build/dist /var/www/html/project
|
|
1018
|
+
|
|
1019
|
+
# Environment variables for nginx config
|
|
1020
|
+
ENV PORT=3001
|
|
1021
|
+
ENV PH_CONNECT_BASE_PATH="/"
|
|
1022
|
+
|
|
1023
|
+
# Copy and setup entrypoint
|
|
1024
|
+
COPY docker/connect-entrypoint.sh /docker-entrypoint.sh
|
|
1025
|
+
RUN chmod +x /docker-entrypoint.sh
|
|
1026
|
+
|
|
1027
|
+
EXPOSE \${PORT}
|
|
1028
|
+
|
|
1029
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \\
|
|
1030
|
+
CMD wget -q --spider http://localhost:\${PORT}/health || exit 1
|
|
1031
|
+
|
|
1032
|
+
ENTRYPOINT ["/docker-entrypoint.sh"]
|
|
1033
|
+
|
|
1034
|
+
# -----------------------------------------------------------------------------
|
|
1035
|
+
# Switchboard final stage - node runtime
|
|
1036
|
+
# -----------------------------------------------------------------------------
|
|
1037
|
+
FROM node:24-alpine AS switchboard
|
|
1038
|
+
|
|
1039
|
+
WORKDIR /app
|
|
1040
|
+
|
|
1041
|
+
# Install runtime dependencies
|
|
1042
|
+
RUN apk add --no-cache curl openssl
|
|
1043
|
+
|
|
1044
|
+
# Setup pnpm
|
|
1045
|
+
ENV PNPM_HOME="/pnpm"
|
|
1046
|
+
ENV PATH="$PNPM_HOME:$PATH"
|
|
1047
|
+
RUN corepack enable && corepack prepare pnpm@latest --activate
|
|
1048
|
+
|
|
1049
|
+
# Configure JSR registry
|
|
1050
|
+
RUN pnpm config set @jsr:registry https://npm.jsr.io
|
|
1051
|
+
|
|
1052
|
+
# Install ph-cmd and prisma globally (needed at runtime)
|
|
1053
|
+
ARG TAG=latest
|
|
1054
|
+
RUN pnpm add -g ph-cmd@$TAG prisma@5.17.0
|
|
1055
|
+
|
|
1056
|
+
# Copy built project from build stage
|
|
1057
|
+
COPY --from=base /app/project /app/project
|
|
1058
|
+
|
|
1059
|
+
WORKDIR /app/project
|
|
1060
|
+
|
|
1061
|
+
# Copy entrypoint
|
|
1062
|
+
COPY docker/switchboard-entrypoint.sh /app/entrypoint.sh
|
|
1063
|
+
RUN chmod +x /app/entrypoint.sh
|
|
1064
|
+
|
|
1065
|
+
# Environment variables
|
|
1066
|
+
ENV NODE_ENV=production
|
|
1067
|
+
ENV PORT=3000
|
|
1068
|
+
ENV DATABASE_URL=""
|
|
1069
|
+
ENV SKIP_DB_MIGRATIONS="false"
|
|
1070
|
+
|
|
1071
|
+
EXPOSE \${PORT}
|
|
1072
|
+
|
|
1073
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=30s --retries=3 \\
|
|
1074
|
+
CMD curl -f http://localhost:\${PORT}/health || exit 1
|
|
1075
|
+
|
|
1076
|
+
ENTRYPOINT ["/app/entrypoint.sh"]
|
|
1077
|
+
`;
|
|
1078
|
+
//#endregion
|
|
1079
|
+
//#region src/templates/boilerplate/docker/nginx.conf.ts
|
|
1080
|
+
const nginxConfTemplate = `user nginx;
|
|
1081
|
+
worker_processes auto;
|
|
1082
|
+
error_log /var/log/nginx/error.log warn;
|
|
1083
|
+
pid /var/run/nginx.pid;
|
|
1084
|
+
|
|
1085
|
+
events {
|
|
1086
|
+
worker_connections 1024;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
http {
|
|
1090
|
+
include /etc/nginx/mime.types;
|
|
1091
|
+
default_type application/octet-stream;
|
|
1092
|
+
|
|
1093
|
+
# Gzip compression
|
|
1094
|
+
gzip on;
|
|
1095
|
+
gzip_vary on;
|
|
1096
|
+
gzip_proxied any;
|
|
1097
|
+
gzip_comp_level 6;
|
|
1098
|
+
gzip_buffers 16 8k;
|
|
1099
|
+
gzip_http_version 1.1;
|
|
1100
|
+
gzip_types text/plain text/css application/javascript application/json image/svg+xml application/xml+rss image/avif;
|
|
1101
|
+
|
|
1102
|
+
server {
|
|
1103
|
+
listen 0.0.0.0:\${PORT};
|
|
1104
|
+
server_name _;
|
|
1105
|
+
root /var/www/html/project;
|
|
1106
|
+
|
|
1107
|
+
# Health check endpoint
|
|
1108
|
+
location /health {
|
|
1109
|
+
access_log off;
|
|
1110
|
+
add_header Content-Type text/plain;
|
|
1111
|
+
return 200 'OK';
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
location \${PH_CONNECT_BASE_PATH}/assets/ {
|
|
1115
|
+
alias /var/www/html/project/assets/;
|
|
1116
|
+
access_log off;
|
|
1117
|
+
log_not_found off;
|
|
1118
|
+
etag off;
|
|
1119
|
+
expires max;
|
|
1120
|
+
add_header Cache-Control "public, max-age=31536000, immutable";
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
location \${PH_CONNECT_BASE_PATH}/fonts/ {
|
|
1124
|
+
alias /var/www/html/project/fonts/;
|
|
1125
|
+
access_log off;
|
|
1126
|
+
log_not_found off;
|
|
1127
|
+
expires max;
|
|
1128
|
+
add_header Cache-Control "public, max-age=31536000, immutable";
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
location = \${PH_CONNECT_BASE_PATH}/service-worker.js {
|
|
1132
|
+
alias /var/www/html/project/service-worker.js;
|
|
1133
|
+
|
|
1134
|
+
access_log off;
|
|
1135
|
+
log_not_found off;
|
|
1136
|
+
etag off;
|
|
1137
|
+
|
|
1138
|
+
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
|
1139
|
+
add_header Pragma "no-cache";
|
|
1140
|
+
add_header Expires "0";
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
# match any file in the root folder, except index.html
|
|
1144
|
+
location ~ \${PH_CONNECT_BASE_PATH}/(?!index\\.html$)([^/]+\\.[a-zA-Z0-9]+)$ {
|
|
1145
|
+
alias /var/www/html/project/$1;
|
|
1146
|
+
access_log off;
|
|
1147
|
+
log_not_found off;
|
|
1148
|
+
etag on;
|
|
1149
|
+
add_header Cache-Control "public, must-revalidate";
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
location \${PH_CONNECT_BASE_PATH} {
|
|
1153
|
+
try_files $uri $uri/ /index.html;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
`;
|
|
1158
|
+
//#endregion
|
|
1159
|
+
//#region src/templates/boilerplate/docker/switchboard-entrypoint.sh.ts
|
|
1160
|
+
const switchboardEntrypointTemplate = `#!/bin/sh
|
|
1161
|
+
set -e
|
|
1162
|
+
|
|
1163
|
+
# Regenerate Prisma client for current platform (fixes darwin-arm64 vs linux-musl-openssl mismatch)
|
|
1164
|
+
echo "[entrypoint] Regenerating Prisma client for current platform..."
|
|
1165
|
+
prisma generate --schema node_modules/document-drive/dist/prisma/schema.prisma
|
|
1166
|
+
|
|
1167
|
+
# Run migrations if DATABASE_URL is postgres and migrations not skipped
|
|
1168
|
+
if [ -n "$DATABASE_URL" ] && echo "$DATABASE_URL" | grep -q "^postgres" && [ "$SKIP_DB_MIGRATIONS" != "true" ]; then
|
|
1169
|
+
echo "[entrypoint] Running Prisma db push..."
|
|
1170
|
+
prisma db push --schema node_modules/document-drive/dist/prisma/schema.prisma --skip-generate
|
|
1171
|
+
echo "[entrypoint] Running migrations..."
|
|
1172
|
+
ph switchboard --migrate
|
|
1173
|
+
fi
|
|
1174
|
+
|
|
1175
|
+
echo "[entrypoint] Starting switchboard on port \${PORT:-3000}..."
|
|
1176
|
+
exec ph switchboard --port \${PORT:-3000}
|
|
1177
|
+
`;
|
|
1178
|
+
//#endregion
|
|
1179
|
+
//#region src/templates/boilerplate/document-models/document-models.ts
|
|
1180
|
+
const documentModelsTemplate = ts`
|
|
1181
|
+
import type { DocumentModelModule } from "document-model";
|
|
1182
|
+
|
|
1183
|
+
export const documentModels: DocumentModelModule[] = [];
|
|
1184
|
+
`.raw;
|
|
1185
|
+
//#endregion
|
|
1186
|
+
//#region src/templates/boilerplate/document-models/index.ts
|
|
1187
|
+
const documentModelsIndexTemplate = "";
|
|
1188
|
+
//#endregion
|
|
1189
|
+
//#region src/templates/boilerplate/editors/editors.ts
|
|
1190
|
+
const editorsTemplate = ts`
|
|
1191
|
+
import type { EditorModule } from "document-model";
|
|
1192
|
+
|
|
1193
|
+
export const editors: EditorModule[] = [];
|
|
1194
|
+
`.raw;
|
|
1195
|
+
//#endregion
|
|
1196
|
+
//#region src/templates/boilerplate/editors/index.ts
|
|
1197
|
+
const editorsIndexTemplate = "";
|
|
1198
|
+
//#endregion
|
|
1199
|
+
//#region src/templates/boilerplate/eslint.config.js.ts
|
|
1200
|
+
const eslintConfigTemplate = js`
|
|
1201
|
+
// @ts-check
|
|
1202
|
+
import { default as eslint } from "@eslint/js";
|
|
1203
|
+
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
|
|
1204
|
+
import reactPlugin from "eslint-plugin-react";
|
|
1205
|
+
import reactHooksPlugin from "eslint-plugin-react-hooks";
|
|
1206
|
+
import { defineConfig, globalIgnores } from "eslint/config";
|
|
1207
|
+
import globals from "globals";
|
|
1208
|
+
import tseslint from "typescript-eslint";
|
|
1209
|
+
|
|
1210
|
+
/** These files are typically ignored by eslint by default, so there is no need to investigate why they are ignored. */
|
|
1211
|
+
const ignoredFiles = [
|
|
1212
|
+
"**/node_modules/",
|
|
1213
|
+
"**/dist/",
|
|
1214
|
+
"**/.ph/",
|
|
1215
|
+
"**/storybook-static/",
|
|
1216
|
+
"**/.vite/",
|
|
1217
|
+
];
|
|
1218
|
+
|
|
1219
|
+
/** Global configs for eslint ignores */
|
|
1220
|
+
const ignored = globalIgnores(ignoredFiles);
|
|
1221
|
+
|
|
1222
|
+
/** Typescript (\`.ts\`) files */
|
|
1223
|
+
const typescriptFiles = ["**/*.ts"];
|
|
1224
|
+
|
|
1225
|
+
/** Typescript React (\`.tsx\`) files */
|
|
1226
|
+
const typescriptReactFiles = ["**/*.tsx"];
|
|
1227
|
+
|
|
1228
|
+
/** Javascript (\`.js\`, \`.cjs\`, \`.mjs\`) files */
|
|
1229
|
+
const javascriptFiles = ["**/*.js", "**/*.cjs", "**/*.mjs"];
|
|
1230
|
+
|
|
1231
|
+
/** Typescript rules that we have chosen to opt out of in general */
|
|
1232
|
+
/** @type {import("eslint").Linter.RulesRecord} */
|
|
1233
|
+
const typescriptRules = {
|
|
1234
|
+
"@typescript-eslint/consistent-type-imports": [
|
|
1235
|
+
"error",
|
|
1236
|
+
{
|
|
1237
|
+
prefer: "type-imports",
|
|
1238
|
+
disallowTypeAnnotations: true,
|
|
1239
|
+
fixStyle: "separate-type-imports",
|
|
1240
|
+
},
|
|
1241
|
+
],
|
|
1242
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
1243
|
+
"@typescript-eslint/no-unused-vars": [
|
|
1244
|
+
"warn",
|
|
1245
|
+
{
|
|
1246
|
+
argsIgnorePattern: "^_",
|
|
1247
|
+
varsIgnorePattern: "^_",
|
|
1248
|
+
caughtErrorsIgnorePattern: "^_",
|
|
1249
|
+
},
|
|
1250
|
+
],
|
|
1251
|
+
"@typescript-eslint/no-unnecessary-condition": "warn",
|
|
1252
|
+
"@typescript-eslint/require-await": "warn",
|
|
1253
|
+
"@typescript-eslint/no-misused-promises": "warn",
|
|
1254
|
+
"@typescript-eslint/no-floating-promises": "warn",
|
|
1255
|
+
"@typescript-eslint/no-empty-object-type": "warn",
|
|
1256
|
+
"@typescript-eslint/no-duplicate-type-constituents": "warn",
|
|
1257
|
+
"@typescript-eslint/restrict-template-expressions": [
|
|
1258
|
+
"warn",
|
|
1259
|
+
{
|
|
1260
|
+
allowNumber: true,
|
|
1261
|
+
},
|
|
1262
|
+
],
|
|
1263
|
+
};
|
|
1264
|
+
|
|
1265
|
+
/** Language options for typescript files
|
|
1266
|
+
@type {import("eslint").Linter.LanguageOptions} */
|
|
1267
|
+
const typescriptLanguageOptions = {
|
|
1268
|
+
sourceType: "module",
|
|
1269
|
+
ecmaVersion: "latest",
|
|
1270
|
+
globals: {
|
|
1271
|
+
...globals.browser,
|
|
1272
|
+
...globals.node,
|
|
1273
|
+
},
|
|
1274
|
+
parserOptions: {
|
|
1275
|
+
projectService: {
|
|
1276
|
+
allowDefaultProject: ["eslint.config.js", "vitest.config.ts"],
|
|
1277
|
+
},
|
|
1278
|
+
tsconfigRootDir: import.meta.dirname,
|
|
1279
|
+
ecmaFeatures: {
|
|
1280
|
+
jsx: true,
|
|
1281
|
+
},
|
|
1282
|
+
},
|
|
1283
|
+
};
|
|
1284
|
+
|
|
1285
|
+
/** React plugins */
|
|
1286
|
+
const reactPlugins = {
|
|
1287
|
+
react: reactPlugin,
|
|
1288
|
+
"react-hooks": reactHooksPlugin,
|
|
1289
|
+
};
|
|
1290
|
+
|
|
1291
|
+
/** React settings */
|
|
1292
|
+
const reactSettings = {
|
|
1293
|
+
react: {
|
|
1294
|
+
version: "detect",
|
|
1295
|
+
},
|
|
1296
|
+
};
|
|
1297
|
+
|
|
1298
|
+
/** Typescript config for both \`.ts\` and \`.tsx\` files */
|
|
1299
|
+
const typescriptConfig = {
|
|
1300
|
+
files: [...typescriptFiles, ...typescriptReactFiles],
|
|
1301
|
+
languageOptions: typescriptLanguageOptions,
|
|
1302
|
+
rules: typescriptRules,
|
|
1303
|
+
};
|
|
1304
|
+
|
|
1305
|
+
/** React config for \`.tsx\` files */
|
|
1306
|
+
const reactConfig = {
|
|
1307
|
+
files: typescriptReactFiles,
|
|
1308
|
+
settings: reactSettings,
|
|
1309
|
+
plugins: reactPlugins,
|
|
1310
|
+
};
|
|
1311
|
+
|
|
1312
|
+
/** Config for javascript files */
|
|
1313
|
+
const javascriptConfig = {
|
|
1314
|
+
// disable type aware linting for js files
|
|
1315
|
+
files: javascriptFiles,
|
|
1316
|
+
extends: [tseslint.configs.disableTypeChecked],
|
|
1317
|
+
};
|
|
1318
|
+
|
|
1319
|
+
/** Recommended config from eslint */
|
|
1320
|
+
const eslintRecommendedConfig = eslint.configs.recommended;
|
|
1321
|
+
|
|
1322
|
+
/** Recommended config from typescript-eslint */
|
|
1323
|
+
const typescriptEsLintRecommendedConfig = [
|
|
1324
|
+
...tseslint.configs.recommendedTypeChecked,
|
|
1325
|
+
];
|
|
1326
|
+
|
|
1327
|
+
/** Main config */
|
|
1328
|
+
export default defineConfig(
|
|
1329
|
+
ignored,
|
|
1330
|
+
eslintRecommendedConfig,
|
|
1331
|
+
typescriptEsLintRecommendedConfig,
|
|
1332
|
+
typescriptConfig,
|
|
1333
|
+
reactConfig,
|
|
1334
|
+
javascriptConfig,
|
|
1335
|
+
eslintPluginPrettierRecommended,
|
|
1336
|
+
);
|
|
1337
|
+
`.raw;
|
|
1338
|
+
//#endregion
|
|
1339
|
+
//#region src/templates/boilerplate/gemini/settings.json.ts
|
|
1340
|
+
const geminiSettingsTemplate = json`
|
|
1341
|
+
{
|
|
1342
|
+
"mcpServers": {
|
|
1343
|
+
"reactor-mcp": {
|
|
1344
|
+
"type": "http",
|
|
1345
|
+
"url": "http://localhost:4001/mcp"
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
`.raw;
|
|
1350
|
+
//#endregion
|
|
1351
|
+
//#region src/templates/boilerplate/github/sync-and-publish.yml.ts
|
|
1352
|
+
const syncAndPublishWorkflowTemplate = yaml`
|
|
1353
|
+
name: Sync and Publish
|
|
1354
|
+
|
|
1355
|
+
on:
|
|
1356
|
+
# Triggered by powerhouse monorepo after release
|
|
1357
|
+
repository_dispatch:
|
|
1358
|
+
types: [powerhouse-release]
|
|
1359
|
+
|
|
1360
|
+
# Manual trigger
|
|
1361
|
+
workflow_dispatch:
|
|
1362
|
+
inputs:
|
|
1363
|
+
channel:
|
|
1364
|
+
description: 'Release channel'
|
|
1365
|
+
required: true
|
|
1366
|
+
type: choice
|
|
1367
|
+
options:
|
|
1368
|
+
- dev
|
|
1369
|
+
- staging
|
|
1370
|
+
- latest
|
|
1371
|
+
default: 'staging'
|
|
1372
|
+
version:
|
|
1373
|
+
description: 'Powerhouse version (e.g., 5.3.0-staging.6)'
|
|
1374
|
+
required: false
|
|
1375
|
+
type: string
|
|
1376
|
+
dry-run:
|
|
1377
|
+
description: 'Dry run (skip publishing)'
|
|
1378
|
+
required: false
|
|
1379
|
+
type: boolean
|
|
1380
|
+
default: false
|
|
1381
|
+
skip-docker:
|
|
1382
|
+
description: 'Skip Docker build and push'
|
|
1383
|
+
required: false
|
|
1384
|
+
type: boolean
|
|
1385
|
+
default: false
|
|
1386
|
+
|
|
1387
|
+
env:
|
|
1388
|
+
NODE_VERSION: '24'
|
|
1389
|
+
PNPM_VERSION: '10'
|
|
1390
|
+
DOCKER_REGISTRY: cr.vetra.io
|
|
1391
|
+
GHCR_REGISTRY: ghcr.io
|
|
1392
|
+
|
|
1393
|
+
jobs:
|
|
1394
|
+
# ==========================================================================
|
|
1395
|
+
# Determine release parameters
|
|
1396
|
+
# ==========================================================================
|
|
1397
|
+
prepare:
|
|
1398
|
+
name: Prepare Release
|
|
1399
|
+
runs-on: ubuntu-latest
|
|
1400
|
+
outputs:
|
|
1401
|
+
channel: \${{ steps.params.outputs.channel }}
|
|
1402
|
+
version: \${{ steps.params.outputs.version }}
|
|
1403
|
+
branch: \${{ steps.params.outputs.branch }}
|
|
1404
|
+
project_name: \${{ steps.params.outputs.project_name }}
|
|
1405
|
+
dry_run: \${{ steps.params.outputs.dry_run }}
|
|
1406
|
+
skip_docker: \${{ steps.params.outputs.skip_docker }}
|
|
1407
|
+
steps:
|
|
1408
|
+
- name: Determine parameters
|
|
1409
|
+
id: params
|
|
1410
|
+
run: |
|
|
1411
|
+
# Get channel from dispatch payload or input
|
|
1412
|
+
if [ "\${{ github.event_name }}" = "repository_dispatch" ]; then
|
|
1413
|
+
CHANNEL="\${{ github.event.client_payload.channel }}"
|
|
1414
|
+
VERSION="\${{ github.event.client_payload.version }}"
|
|
1415
|
+
DRY_RUN="false"
|
|
1416
|
+
SKIP_DOCKER="false"
|
|
1417
|
+
else
|
|
1418
|
+
CHANNEL="\${{ inputs.channel }}"
|
|
1419
|
+
VERSION="\${{ inputs.version }}"
|
|
1420
|
+
DRY_RUN="\${{ inputs.dry-run }}"
|
|
1421
|
+
SKIP_DOCKER="\${{ inputs.skip-docker }}"
|
|
1422
|
+
fi
|
|
1423
|
+
|
|
1424
|
+
# Default channel to staging if not set
|
|
1425
|
+
CHANNEL="\${CHANNEL:-staging}"
|
|
1426
|
+
|
|
1427
|
+
# Determine branch from channel
|
|
1428
|
+
case "\$CHANNEL" in
|
|
1429
|
+
dev) BRANCH="dev" ;;
|
|
1430
|
+
staging) BRANCH="staging" ;;
|
|
1431
|
+
latest|main) BRANCH="main" ;;
|
|
1432
|
+
*) BRANCH="staging" ;;
|
|
1433
|
+
esac
|
|
1434
|
+
|
|
1435
|
+
# Use DOCKER_PROJECT secret if set, otherwise extract from repository name
|
|
1436
|
+
if [ -n "\${{ secrets.DOCKER_PROJECT }}" ]; then
|
|
1437
|
+
PROJECT_NAME="\${{ secrets.DOCKER_PROJECT }}"
|
|
1438
|
+
else
|
|
1439
|
+
PROJECT_NAME="\${GITHUB_REPOSITORY#*/}"
|
|
1440
|
+
fi
|
|
1441
|
+
|
|
1442
|
+
echo "channel=\$CHANNEL" >> \$GITHUB_OUTPUT
|
|
1443
|
+
echo "version=\$VERSION" >> \$GITHUB_OUTPUT
|
|
1444
|
+
echo "branch=\$BRANCH" >> \$GITHUB_OUTPUT
|
|
1445
|
+
echo "project_name=\$PROJECT_NAME" >> \$GITHUB_OUTPUT
|
|
1446
|
+
echo "dry_run=\$DRY_RUN" >> \$GITHUB_OUTPUT
|
|
1447
|
+
echo "skip_docker=\$SKIP_DOCKER" >> \$GITHUB_OUTPUT
|
|
1448
|
+
|
|
1449
|
+
echo "Channel: \$CHANNEL"
|
|
1450
|
+
echo "Version: \$VERSION"
|
|
1451
|
+
echo "Branch: \$BRANCH"
|
|
1452
|
+
echo "Project: \$PROJECT_NAME"
|
|
1453
|
+
echo "Dry Run: \$DRY_RUN"
|
|
1454
|
+
echo "Skip Docker: \$SKIP_DOCKER"
|
|
1455
|
+
|
|
1456
|
+
# ==========================================================================
|
|
1457
|
+
# Update dependencies and publish to npm
|
|
1458
|
+
# ==========================================================================
|
|
1459
|
+
update-and-publish:
|
|
1460
|
+
name: Update & Publish NPM
|
|
1461
|
+
needs: prepare
|
|
1462
|
+
runs-on: ubuntu-latest
|
|
1463
|
+
permissions:
|
|
1464
|
+
contents: write
|
|
1465
|
+
id-token: write
|
|
1466
|
+
outputs:
|
|
1467
|
+
new_version: \${{ steps.version.outputs.new_version }}
|
|
1468
|
+
steps:
|
|
1469
|
+
- name: Checkout repository
|
|
1470
|
+
uses: actions/checkout@v4
|
|
1471
|
+
with:
|
|
1472
|
+
ref: \${{ needs.prepare.outputs.branch }}
|
|
1473
|
+
fetch-depth: 0
|
|
1474
|
+
token: \${{ secrets.GITHUB_TOKEN }}
|
|
1475
|
+
|
|
1476
|
+
- name: Install pnpm
|
|
1477
|
+
uses: pnpm/action-setup@v4
|
|
1478
|
+
with:
|
|
1479
|
+
version: \${{ env.PNPM_VERSION }}
|
|
1480
|
+
|
|
1481
|
+
- name: Install Node.js
|
|
1482
|
+
uses: actions/setup-node@v4
|
|
1483
|
+
with:
|
|
1484
|
+
node-version: \${{ env.NODE_VERSION }}
|
|
1485
|
+
cache: 'pnpm'
|
|
1486
|
+
|
|
1487
|
+
- name: Configure git
|
|
1488
|
+
run: |
|
|
1489
|
+
git config user.name "github-actions[bot]"
|
|
1490
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
1491
|
+
|
|
1492
|
+
- name: Install ph-cli
|
|
1493
|
+
run: |
|
|
1494
|
+
CHANNEL="\${{ needs.prepare.outputs.channel }}"
|
|
1495
|
+
case "\$CHANNEL" in
|
|
1496
|
+
dev) pnpm add -g @powerhousedao/ph-cli@dev ;;
|
|
1497
|
+
staging) pnpm add -g @powerhousedao/ph-cli@staging ;;
|
|
1498
|
+
*) pnpm add -g @powerhousedao/ph-cli@latest ;;
|
|
1499
|
+
esac
|
|
1500
|
+
|
|
1501
|
+
- name: Update Powerhouse dependencies
|
|
1502
|
+
run: ph update
|
|
1503
|
+
|
|
1504
|
+
- name: Install dependencies
|
|
1505
|
+
run: pnpm install
|
|
1506
|
+
|
|
1507
|
+
- name: Build package
|
|
1508
|
+
run: pnpm build
|
|
1509
|
+
|
|
1510
|
+
- name: Run tests
|
|
1511
|
+
run: pnpm test || true
|
|
1512
|
+
continue-on-error: true
|
|
1513
|
+
|
|
1514
|
+
- name: Bump version
|
|
1515
|
+
id: version
|
|
1516
|
+
run: |
|
|
1517
|
+
CHANNEL="\${{ needs.prepare.outputs.channel }}"
|
|
1518
|
+
CURRENT_VERSION=\$(node -p "require('./package.json').version")
|
|
1519
|
+
|
|
1520
|
+
# Determine new version
|
|
1521
|
+
if [ "\$CHANNEL" = "latest" ] || [ "\$CHANNEL" = "main" ]; then
|
|
1522
|
+
# For production, use patch bump
|
|
1523
|
+
npm version patch --no-git-tag-version
|
|
1524
|
+
else
|
|
1525
|
+
# For dev/staging, use prerelease
|
|
1526
|
+
npm version prerelease --preid=\$CHANNEL --no-git-tag-version
|
|
1527
|
+
fi
|
|
1528
|
+
|
|
1529
|
+
NEW_VERSION=\$(node -p "require('./package.json').version")
|
|
1530
|
+
echo "new_version=\$NEW_VERSION" >> \$GITHUB_OUTPUT
|
|
1531
|
+
echo "Bumped version: \$CURRENT_VERSION -> \$NEW_VERSION"
|
|
1532
|
+
|
|
1533
|
+
- name: Commit changes
|
|
1534
|
+
run: |
|
|
1535
|
+
git add package.json pnpm-lock.yaml
|
|
1536
|
+
git commit -m "chore: sync powerhouse dependencies to \${{ needs.prepare.outputs.version }}
|
|
1537
|
+
|
|
1538
|
+
- Updated to powerhouse \${{ needs.prepare.outputs.version }}
|
|
1539
|
+
- Bumped version to \${{ steps.version.outputs.new_version }}" || echo "No changes to commit"
|
|
1540
|
+
|
|
1541
|
+
- name: Push changes
|
|
1542
|
+
if: needs.prepare.outputs.dry_run != 'true'
|
|
1543
|
+
run: git push
|
|
1544
|
+
|
|
1545
|
+
- name: Setup npm for publishing
|
|
1546
|
+
if: needs.prepare.outputs.dry_run != 'true' && secrets.NPM_ACCESS_TOKEN != ''
|
|
1547
|
+
uses: actions/setup-node@v4
|
|
1548
|
+
with:
|
|
1549
|
+
node-version: \${{ env.NODE_VERSION }}
|
|
1550
|
+
registry-url: 'https://registry.npmjs.org'
|
|
1551
|
+
|
|
1552
|
+
- name: Publish to npm with provenance
|
|
1553
|
+
if: needs.prepare.outputs.dry_run != 'true' && secrets.NPM_ACCESS_TOKEN != ''
|
|
1554
|
+
env:
|
|
1555
|
+
NODE_AUTH_TOKEN: \${{ secrets.NPM_ACCESS_TOKEN }}
|
|
1556
|
+
NPM_CONFIG_PROVENANCE: true
|
|
1557
|
+
run: |
|
|
1558
|
+
CHANNEL="\${{ needs.prepare.outputs.channel }}"
|
|
1559
|
+
if [ "\$CHANNEL" = "latest" ] || [ "\$CHANNEL" = "main" ]; then
|
|
1560
|
+
pnpm publish --access public --tag latest --no-git-checks
|
|
1561
|
+
else
|
|
1562
|
+
pnpm publish --access public --tag \$CHANNEL --no-git-checks
|
|
1563
|
+
fi
|
|
1564
|
+
|
|
1565
|
+
- name: Create git tag
|
|
1566
|
+
if: needs.prepare.outputs.dry_run != 'true'
|
|
1567
|
+
run: |
|
|
1568
|
+
git tag "v\${{ steps.version.outputs.new_version }}"
|
|
1569
|
+
git push origin "v\${{ steps.version.outputs.new_version }}"
|
|
1570
|
+
|
|
1571
|
+
# ==========================================================================
|
|
1572
|
+
# Build and push Docker images
|
|
1573
|
+
# ==========================================================================
|
|
1574
|
+
build-docker:
|
|
1575
|
+
name: Build Docker Images
|
|
1576
|
+
needs: [prepare, update-and-publish]
|
|
1577
|
+
if: |
|
|
1578
|
+
needs.prepare.outputs.skip_docker != 'true' &&
|
|
1579
|
+
needs.prepare.outputs.dry_run != 'true' &&
|
|
1580
|
+
secrets.DOCKER_USERNAME != '' &&
|
|
1581
|
+
secrets.DOCKER_PASSWORD != ''
|
|
1582
|
+
runs-on: ubuntu-latest
|
|
1583
|
+
permissions:
|
|
1584
|
+
contents: read
|
|
1585
|
+
packages: write
|
|
1586
|
+
strategy:
|
|
1587
|
+
matrix:
|
|
1588
|
+
target: [connect, switchboard]
|
|
1589
|
+
steps:
|
|
1590
|
+
- name: Checkout repository
|
|
1591
|
+
uses: actions/checkout@v4
|
|
1592
|
+
with:
|
|
1593
|
+
ref: \${{ needs.prepare.outputs.branch }}
|
|
1594
|
+
|
|
1595
|
+
- name: Pull latest changes
|
|
1596
|
+
run: git pull origin \${{ needs.prepare.outputs.branch }}
|
|
1597
|
+
|
|
1598
|
+
- name: Set up Docker Buildx
|
|
1599
|
+
uses: docker/setup-buildx-action@v3
|
|
1600
|
+
|
|
1601
|
+
- name: Ensure Docker project exists
|
|
1602
|
+
run: |
|
|
1603
|
+
PROJECT_NAME="\${{ needs.prepare.outputs.project_name }}"
|
|
1604
|
+
|
|
1605
|
+
# Check if project exists, create if not
|
|
1606
|
+
STATUS=\$(curl -s -o /dev/null -w "%{http_code}" \\
|
|
1607
|
+
-u "\${{ secrets.DOCKER_USERNAME }}:\${{ secrets.DOCKER_PASSWORD }}" \\
|
|
1608
|
+
"https://\${{ env.DOCKER_REGISTRY }}/api/v2.0/projects?name=\${PROJECT_NAME}")
|
|
1609
|
+
|
|
1610
|
+
if [ "\$STATUS" = "200" ]; then
|
|
1611
|
+
# Check if the project is in the response
|
|
1612
|
+
EXISTS=\$(curl -s \\
|
|
1613
|
+
-u "\${{ secrets.DOCKER_USERNAME }}:\${{ secrets.DOCKER_PASSWORD }}" \\
|
|
1614
|
+
"https://\${{ env.DOCKER_REGISTRY }}/api/v2.0/projects?name=\${PROJECT_NAME}" | \\
|
|
1615
|
+
jq -r ".[] | select(.name==\\"\${PROJECT_NAME}\\") | .name")
|
|
1616
|
+
|
|
1617
|
+
if [ "\$EXISTS" = "\$PROJECT_NAME" ]; then
|
|
1618
|
+
echo "Project \${PROJECT_NAME} already exists"
|
|
1619
|
+
else
|
|
1620
|
+
echo "Creating project \${PROJECT_NAME}..."
|
|
1621
|
+
curl -X POST \\
|
|
1622
|
+
-u "\${{ secrets.DOCKER_USERNAME }}:\${{ secrets.DOCKER_PASSWORD }}" \\
|
|
1623
|
+
-H "Content-Type: application/json" \\
|
|
1624
|
+
-d "{\\"project_name\\": \\"\${PROJECT_NAME}\\", \\"public\\": false}" \\
|
|
1625
|
+
"https://\${{ env.DOCKER_REGISTRY }}/api/v2.0/projects"
|
|
1626
|
+
fi
|
|
1627
|
+
else
|
|
1628
|
+
echo "Creating project \${PROJECT_NAME}..."
|
|
1629
|
+
curl -X POST \\
|
|
1630
|
+
-u "\${{ secrets.DOCKER_USERNAME }}:\${{ secrets.DOCKER_PASSWORD }}" \\
|
|
1631
|
+
-H "Content-Type: application/json" \\
|
|
1632
|
+
-d "{\\"project_name\\": \\"\${PROJECT_NAME}\\", \\"public\\": false}" \\
|
|
1633
|
+
"https://\${{ env.DOCKER_REGISTRY }}/api/v2.0/projects"
|
|
1634
|
+
fi
|
|
1635
|
+
|
|
1636
|
+
- name: Login to GitHub Container Registry
|
|
1637
|
+
uses: docker/login-action@v3
|
|
1638
|
+
with:
|
|
1639
|
+
registry: \${{ env.GHCR_REGISTRY }}
|
|
1640
|
+
username: \${{ github.actor }}
|
|
1641
|
+
password: \${{ secrets.GITHUB_TOKEN }}
|
|
1642
|
+
|
|
1643
|
+
- name: Login to Docker Registry
|
|
1644
|
+
uses: docker/login-action@v3
|
|
1645
|
+
with:
|
|
1646
|
+
registry: \${{ env.DOCKER_REGISTRY }}
|
|
1647
|
+
username: \${{ secrets.DOCKER_USERNAME }}
|
|
1648
|
+
password: \${{ secrets.DOCKER_PASSWORD }}
|
|
1649
|
+
|
|
1650
|
+
- name: Extract package name
|
|
1651
|
+
id: package
|
|
1652
|
+
run: |
|
|
1653
|
+
PACKAGE_NAME=\$(node -p "require('./package.json').name")
|
|
1654
|
+
echo "name=\$PACKAGE_NAME" >> \$GITHUB_OUTPUT
|
|
1655
|
+
|
|
1656
|
+
- name: Determine image tags
|
|
1657
|
+
id: tags
|
|
1658
|
+
run: |
|
|
1659
|
+
VERSION="\${{ needs.update-and-publish.outputs.new_version }}"
|
|
1660
|
+
CHANNEL="\${{ needs.prepare.outputs.channel }}"
|
|
1661
|
+
PROJECT="\${{ needs.prepare.outputs.project_name }}"
|
|
1662
|
+
TARGET="\${{ matrix.target }}"
|
|
1663
|
+
|
|
1664
|
+
# GHCR tags
|
|
1665
|
+
GHCR_BASE="\${{ env.GHCR_REGISTRY }}/\${{ github.repository_owner }}/\${PROJECT}/\${TARGET}"
|
|
1666
|
+
|
|
1667
|
+
# Docker registry tags
|
|
1668
|
+
DOCKER_BASE="\${{ env.DOCKER_REGISTRY }}/\${PROJECT}/\${TARGET}"
|
|
1669
|
+
|
|
1670
|
+
# Build tag list
|
|
1671
|
+
TAGS="\${GHCR_BASE}:v\${VERSION}"
|
|
1672
|
+
TAGS="\${TAGS},\${DOCKER_BASE}:v\${VERSION}"
|
|
1673
|
+
|
|
1674
|
+
# Add channel tag
|
|
1675
|
+
if [ "\$CHANNEL" = "latest" ] || [ "\$CHANNEL" = "main" ]; then
|
|
1676
|
+
TAGS="\${TAGS},\${GHCR_BASE}:latest"
|
|
1677
|
+
TAGS="\${TAGS},\${DOCKER_BASE}:latest"
|
|
1678
|
+
else
|
|
1679
|
+
TAGS="\${TAGS},\${GHCR_BASE}:\${CHANNEL}"
|
|
1680
|
+
TAGS="\${TAGS},\${DOCKER_BASE}:\${CHANNEL}"
|
|
1681
|
+
fi
|
|
1682
|
+
|
|
1683
|
+
echo "tags=\$TAGS" >> \$GITHUB_OUTPUT
|
|
1684
|
+
echo "Image tags: \$TAGS"
|
|
1685
|
+
|
|
1686
|
+
- name: Build and push \${{ matrix.target }}
|
|
1687
|
+
uses: docker/build-push-action@v5
|
|
1688
|
+
with:
|
|
1689
|
+
context: .
|
|
1690
|
+
file: ./Dockerfile
|
|
1691
|
+
target: \${{ matrix.target }}
|
|
1692
|
+
push: true
|
|
1693
|
+
tags: \${{ steps.tags.outputs.tags }}
|
|
1694
|
+
build-args: |
|
|
1695
|
+
TAG=\${{ needs.prepare.outputs.channel == 'latest' && 'latest' || needs.prepare.outputs.version }}
|
|
1696
|
+
PACKAGE_NAME=\${{ steps.package.outputs.name }}
|
|
1697
|
+
PH_CONNECT_BASE_PATH=/
|
|
1698
|
+
cache-from: type=gha
|
|
1699
|
+
cache-to: type=gha,mode=max
|
|
1700
|
+
|
|
1701
|
+
# ==========================================================================
|
|
1702
|
+
# Summary
|
|
1703
|
+
# ==========================================================================
|
|
1704
|
+
summary:
|
|
1705
|
+
name: Release Summary
|
|
1706
|
+
needs: [prepare, update-and-publish, build-docker]
|
|
1707
|
+
if: always()
|
|
1708
|
+
runs-on: ubuntu-latest
|
|
1709
|
+
steps:
|
|
1710
|
+
- name: Summary
|
|
1711
|
+
run: |
|
|
1712
|
+
echo "## Release Summary" >> \$GITHUB_STEP_SUMMARY
|
|
1713
|
+
echo "" >> \$GITHUB_STEP_SUMMARY
|
|
1714
|
+
echo "| Parameter | Value |" >> \$GITHUB_STEP_SUMMARY
|
|
1715
|
+
echo "|-----------|-------|" >> \$GITHUB_STEP_SUMMARY
|
|
1716
|
+
echo "| Channel | \${{ needs.prepare.outputs.channel }} |" >> \$GITHUB_STEP_SUMMARY
|
|
1717
|
+
echo "| Branch | \${{ needs.prepare.outputs.branch }} |" >> \$GITHUB_STEP_SUMMARY
|
|
1718
|
+
echo "| Powerhouse Version | \${{ needs.prepare.outputs.version }} |" >> \$GITHUB_STEP_SUMMARY
|
|
1719
|
+
echo "| Package Version | \${{ needs.update-and-publish.outputs.new_version }} |" >> \$GITHUB_STEP_SUMMARY
|
|
1720
|
+
echo "| Dry Run | \${{ needs.prepare.outputs.dry_run }} |" >> \$GITHUB_STEP_SUMMARY
|
|
1721
|
+
echo "" >> \$GITHUB_STEP_SUMMARY
|
|
1722
|
+
echo "### Docker Images" >> \$GITHUB_STEP_SUMMARY
|
|
1723
|
+
echo "- \\\`\${{ env.DOCKER_REGISTRY }}/\${{ needs.prepare.outputs.project_name }}/connect:v\${{ needs.update-and-publish.outputs.new_version }}\\\`" >> \$GITHUB_STEP_SUMMARY
|
|
1724
|
+
echo "- \\\`\${{ env.DOCKER_REGISTRY }}/\${{ needs.prepare.outputs.project_name }}/switchboard:v\${{ needs.update-and-publish.outputs.new_version }}\\\`" >> \$GITHUB_STEP_SUMMARY
|
|
1725
|
+
`.raw;
|
|
1726
|
+
//#endregion
|
|
1727
|
+
//#region src/templates/boilerplate/gitignore.ts
|
|
1728
|
+
const gitIgnoreTemplate = `
|
|
1729
|
+
dist
|
|
1730
|
+
coverage
|
|
1731
|
+
node_modules
|
|
1732
|
+
.eslintcache
|
|
1733
|
+
.env.local
|
|
1734
|
+
|
|
1735
|
+
.ph
|
|
1736
|
+
projects-import.js
|
|
1737
|
+
`;
|
|
1738
|
+
//#endregion
|
|
1739
|
+
//#region \0@oxc-project+runtime@0.115.0/helpers/taggedTemplateLiteral.js
|
|
1740
|
+
function _taggedTemplateLiteral(e, t) {
|
|
1741
|
+
return t || (t = e.slice(0)), Object.freeze(Object.defineProperties(e, { raw: { value: Object.freeze(t) } }));
|
|
1742
|
+
}
|
|
1743
|
+
//#endregion
|
|
1744
|
+
//#region src/templates/boilerplate/index.html.ts
|
|
1745
|
+
var _templateObject$1;
|
|
1746
|
+
const indexHtmlTemplate = html(_templateObject$1 || (_templateObject$1 = _taggedTemplateLiteral(["<!DOCTYPE html>\n <html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <meta\n name=\"description\"\n content=\"Connect is a hub for your most important documents and processes, translated into software. Easily capture data in a structured way with dedicated business process packages. Collaborate on shared documents with ease while using your preferred storage solution (decentralized, centralized, or local).\"\n />\n <title>Powerhouse Connect</title>\n <link rel=\"icon\" href=\"/icon.ico\" />\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin />\n <link\n href=\"https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap\"\n rel=\"stylesheet\"\n />\n </head>\n <body>\n <div id=\"root\"></div>\n <link href=\"/style.css\" rel=\"stylesheet\" />\n <script type=\"module\" src=\"/main.tsx\"><\/script>\n </body>\n </html> "]))).raw;
|
|
1747
|
+
//#endregion
|
|
1748
|
+
//#region src/templates/boilerplate/index.html.legacy.ts
|
|
1749
|
+
var _templateObject;
|
|
1750
|
+
const legacyIndexHtmlTemplate = html(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n <!DOCTYPE html>\n <html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <meta\n name=\"description\"\n content=\"Connect is a hub for your most important documents and processes, translated into software. Easily capture data in a structured way with dedicated business process packages. Collaborate on shared documents with ease while using your preferred storage solution (decentralized, centralized, or local).\"\n />\n <title>Powerhouse Connect</title>\n <link rel=\"icon\" href=\"/icon.ico\" />\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin />\n <link\n href=\"https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap\"\n rel=\"stylesheet\"\n />\n <style>\n @import \"tailwindcss\";\n @import \"@powerhousedao/design-system/style.css\";\n @import \"@powerhousedao/document-engineering/style.css\";\n @source \"./node_modules/@powerhousedao/connect\";\n </style>\n </head>\n <body>\n <div id=\"root\"></div>\n <script type=\"module\">\n // initializes Connect on '<div id=\"root\"></div>'\n import \"@powerhousedao/connect/main.js\";\n <\/script>\n </body>\n </html>\n"]))).raw;
|
|
1751
|
+
//#endregion
|
|
1752
|
+
//#region src/templates/boilerplate/index.ts
|
|
1753
|
+
const indexTsTemplate = ts`
|
|
1754
|
+
import type { Manifest } from "document-model";
|
|
1755
|
+
import manifestJson from "./powerhouse.manifest.json" with { type: "json" };
|
|
1756
|
+
export { documentModels } from "./document-models/document-models.js";
|
|
1757
|
+
export { upgradeManifests } from "./document-models/upgrade-manifests.js";
|
|
1758
|
+
export { editors } from "./editors/editors.js";
|
|
1759
|
+
export { processorFactory } from "./processors/factory.js";
|
|
1760
|
+
export const manifest: Manifest = manifestJson;
|
|
1761
|
+
`.raw;
|
|
1762
|
+
//#endregion
|
|
1763
|
+
//#region src/templates/boilerplate/LICENSE.ts
|
|
1764
|
+
const licenseTemplate = `
|
|
1765
|
+
GNU AFFERO GENERAL PUBLIC LICENSE
|
|
1766
|
+
Version 3, 19 November 2007
|
|
1767
|
+
|
|
1768
|
+
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
|
1769
|
+
Everyone is permitted to copy and distribute verbatim copies
|
|
1770
|
+
of this license document, but changing it is not allowed.
|
|
1771
|
+
|
|
1772
|
+
Preamble
|
|
1773
|
+
|
|
1774
|
+
The GNU Affero General Public License is a free, copyleft license for
|
|
1775
|
+
software and other kinds of works, specifically designed to ensure
|
|
1776
|
+
cooperation with the community in the case of network server software.
|
|
1777
|
+
|
|
1778
|
+
The licenses for most software and other practical works are designed
|
|
1779
|
+
to take away your freedom to share and change the works. By contrast,
|
|
1780
|
+
our General Public Licenses are intended to guarantee your freedom to
|
|
1781
|
+
share and change all versions of a program--to make sure it remains free
|
|
1782
|
+
software for all its users.
|
|
1783
|
+
|
|
1784
|
+
When we speak of free software, we are referring to freedom, not
|
|
1785
|
+
price. Our General Public Licenses are designed to make sure that you
|
|
1786
|
+
have the freedom to distribute copies of free software (and charge for
|
|
1787
|
+
them if you wish), that you receive source code or can get it if you
|
|
1788
|
+
want it, that you can change the software or use pieces of it in new
|
|
1789
|
+
free programs, and that you know you can do these things.
|
|
1790
|
+
|
|
1791
|
+
Developers that use our General Public Licenses protect your rights
|
|
1792
|
+
with two steps: (1) assert copyright on the software, and (2) offer
|
|
1793
|
+
you this License which gives you legal permission to copy, distribute
|
|
1794
|
+
and/or modify the software.
|
|
1795
|
+
|
|
1796
|
+
A secondary benefit of defending all users' freedom is that
|
|
1797
|
+
improvements made in alternate versions of the program, if they
|
|
1798
|
+
receive widespread use, become available for other developers to
|
|
1799
|
+
incorporate. Many developers of free software are heartened and
|
|
1800
|
+
encouraged by the resulting cooperation. However, in the case of
|
|
1801
|
+
software used on network servers, this result may fail to come about.
|
|
1802
|
+
The GNU General Public License permits making a modified version and
|
|
1803
|
+
letting the public access it on a server without ever releasing its
|
|
1804
|
+
source code to the public.
|
|
1805
|
+
|
|
1806
|
+
The GNU Affero General Public License is designed specifically to
|
|
1807
|
+
ensure that, in such cases, the modified source code becomes available
|
|
1808
|
+
to the community. It requires the operator of a network server to
|
|
1809
|
+
provide the source code of the modified version running there to the
|
|
1810
|
+
users of that server. Therefore, public use of a modified version, on
|
|
1811
|
+
a publicly accessible server, gives the public access to the source
|
|
1812
|
+
code of the modified version.
|
|
1813
|
+
|
|
1814
|
+
An older license, called the Affero General Public License and
|
|
1815
|
+
published by Affero, was designed to accomplish similar goals. This is
|
|
1816
|
+
a different license, not a version of the Affero GPL, but Affero has
|
|
1817
|
+
released a new version of the Affero GPL which permits relicensing under
|
|
1818
|
+
this license.
|
|
1819
|
+
|
|
1820
|
+
The precise terms and conditions for copying, distribution and
|
|
1821
|
+
modification follow.
|
|
1822
|
+
|
|
1823
|
+
TERMS AND CONDITIONS
|
|
1824
|
+
|
|
1825
|
+
0. Definitions.
|
|
1826
|
+
|
|
1827
|
+
"This License" refers to version 3 of the GNU Affero General Public License.
|
|
1828
|
+
|
|
1829
|
+
"Copyright" also means copyright-like laws that apply to other kinds of
|
|
1830
|
+
works, such as semiconductor masks.
|
|
1831
|
+
|
|
1832
|
+
"The Program" refers to any copyrightable work licensed under this
|
|
1833
|
+
License. Each licensee is addressed as "you". "Licensees" and
|
|
1834
|
+
"recipients" may be individuals or organizations.
|
|
1835
|
+
|
|
1836
|
+
To "modify" a work means to copy from or adapt all or part of the work
|
|
1837
|
+
in a fashion requiring copyright permission, other than the making of an
|
|
1838
|
+
exact copy. The resulting work is called a "modified version" of the
|
|
1839
|
+
earlier work or a work "based on" the earlier work.
|
|
1840
|
+
|
|
1841
|
+
A "covered work" means either the unmodified Program or a work based
|
|
1842
|
+
on the Program.
|
|
1843
|
+
|
|
1844
|
+
To "propagate" a work means to do anything with it that, without
|
|
1845
|
+
permission, would make you directly or secondarily liable for
|
|
1846
|
+
infringement under applicable copyright law, except executing it on a
|
|
1847
|
+
computer or modifying a private copy. Propagation includes copying,
|
|
1848
|
+
distribution (with or without modification), making available to the
|
|
1849
|
+
public, and in some countries other activities as well.
|
|
1850
|
+
|
|
1851
|
+
To "convey" a work means any kind of propagation that enables other
|
|
1852
|
+
parties to make or receive copies. Mere interaction with a user through
|
|
1853
|
+
a computer network, with no transfer of a copy, is not conveying.
|
|
1854
|
+
|
|
1855
|
+
An interactive user interface displays "Appropriate Legal Notices"
|
|
1856
|
+
to the extent that it includes a convenient and prominently visible
|
|
1857
|
+
feature that (1) displays an appropriate copyright notice, and (2)
|
|
1858
|
+
tells the user that there is no warranty for the work (except to the
|
|
1859
|
+
extent that warranties are provided), that licensees may convey the
|
|
1860
|
+
work under this License, and how to view a copy of this License. If
|
|
1861
|
+
the interface presents a list of user commands or options, such as a
|
|
1862
|
+
menu, a prominent item in the list meets this criterion.
|
|
1863
|
+
|
|
1864
|
+
1. Source Code.
|
|
1865
|
+
|
|
1866
|
+
The "source code" for a work means the preferred form of the work
|
|
1867
|
+
for making modifications to it. "Object code" means any non-source
|
|
1868
|
+
form of a work.
|
|
1869
|
+
|
|
1870
|
+
A "Standard Interface" means an interface that either is an official
|
|
1871
|
+
standard defined by a recognized standards body, or, in the case of
|
|
1872
|
+
interfaces specified for a particular programming language, one that
|
|
1873
|
+
is widely used among developers working in that language.
|
|
1874
|
+
|
|
1875
|
+
The "System Libraries" of an executable work include anything, other
|
|
1876
|
+
than the work as a whole, that (a) is included in the normal form of
|
|
1877
|
+
packaging a Major Component, but which is not part of that Major
|
|
1878
|
+
Component, and (b) serves only to enable use of the work with that
|
|
1879
|
+
Major Component, or to implement a Standard Interface for which an
|
|
1880
|
+
implementation is available to the public in source code form. A
|
|
1881
|
+
"Major Component", in this context, means a major essential component
|
|
1882
|
+
(kernel, window system, and so on) of the specific operating system
|
|
1883
|
+
(if any) on which the executable work runs, or a compiler used to
|
|
1884
|
+
produce the work, or an object code interpreter used to run it.
|
|
1885
|
+
|
|
1886
|
+
The "Corresponding Source" for a work in object code form means all
|
|
1887
|
+
the source code needed to generate, install, and (for an executable
|
|
1888
|
+
work) run the object code and to modify the work, including scripts to
|
|
1889
|
+
control those activities. However, it does not include the work's
|
|
1890
|
+
System Libraries, or general-purpose tools or generally available free
|
|
1891
|
+
programs which are used unmodified in performing those activities but
|
|
1892
|
+
which are not part of the work. For example, Corresponding Source
|
|
1893
|
+
includes interface definition files associated with source files for
|
|
1894
|
+
the work, and the source code for shared libraries and dynamically
|
|
1895
|
+
linked subprograms that the work is specifically designed to require,
|
|
1896
|
+
such as by intimate data communication or control flow between those
|
|
1897
|
+
subprograms and other parts of the work.
|
|
1898
|
+
|
|
1899
|
+
The Corresponding Source need not include anything that users
|
|
1900
|
+
can regenerate automatically from other parts of the Corresponding
|
|
1901
|
+
Source.
|
|
1902
|
+
|
|
1903
|
+
The Corresponding Source for a work in source code form is that
|
|
1904
|
+
same work.
|
|
1905
|
+
|
|
1906
|
+
2. Basic Permissions.
|
|
1907
|
+
|
|
1908
|
+
All rights granted under this License are granted for the term of
|
|
1909
|
+
copyright on the Program, and are irrevocable provided the stated
|
|
1910
|
+
conditions are met. This License explicitly affirms your unlimited
|
|
1911
|
+
permission to run the unmodified Program. The output from running a
|
|
1912
|
+
covered work is covered by this License only if the output, given its
|
|
1913
|
+
content, constitutes a covered work. This License acknowledges your
|
|
1914
|
+
rights of fair use or other equivalent, as provided by copyright law.
|
|
1915
|
+
|
|
1916
|
+
You may make, run and propagate covered works that you do not
|
|
1917
|
+
convey, without conditions so long as your license otherwise remains
|
|
1918
|
+
in force. You may convey covered works to others for the sole purpose
|
|
1919
|
+
of having them make modifications exclusively for you, or provide you
|
|
1920
|
+
with facilities for running those works, provided that you comply with
|
|
1921
|
+
the terms of this License in conveying all material for which you do
|
|
1922
|
+
not control copyright. Those thus making or running the covered works
|
|
1923
|
+
for you must do so exclusively on your behalf, under your direction
|
|
1924
|
+
and control, on terms that prohibit them from making any copies of
|
|
1925
|
+
your copyrighted material outside their relationship with you.
|
|
1926
|
+
|
|
1927
|
+
Conveying under any other circumstances is permitted solely under
|
|
1928
|
+
the conditions stated below. Sublicensing is not allowed; section 10
|
|
1929
|
+
makes it unnecessary.
|
|
1930
|
+
|
|
1931
|
+
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
|
1932
|
+
|
|
1933
|
+
No covered work shall be deemed part of an effective technological
|
|
1934
|
+
measure under any applicable law fulfilling obligations under article
|
|
1935
|
+
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
|
1936
|
+
similar laws prohibiting or restricting circumvention of such
|
|
1937
|
+
measures.
|
|
1938
|
+
|
|
1939
|
+
When you convey a covered work, you waive any legal power to forbid
|
|
1940
|
+
circumvention of technological measures to the extent such circumvention
|
|
1941
|
+
is effected by exercising rights under this License with respect to
|
|
1942
|
+
the covered work, and you disclaim any intention to limit operation or
|
|
1943
|
+
modification of the work as a means of enforcing, against the work's
|
|
1944
|
+
users, your or third parties' legal rights to forbid circumvention of
|
|
1945
|
+
technological measures.
|
|
1946
|
+
|
|
1947
|
+
4. Conveying Verbatim Copies.
|
|
1948
|
+
|
|
1949
|
+
You may convey verbatim copies of the Program's source code as you
|
|
1950
|
+
receive it, in any medium, provided that you conspicuously and
|
|
1951
|
+
appropriately publish on each copy an appropriate copyright notice;
|
|
1952
|
+
keep intact all notices stating that this License and any
|
|
1953
|
+
non-permissive terms added in accord with section 7 apply to the code;
|
|
1954
|
+
keep intact all notices of the absence of any warranty; and give all
|
|
1955
|
+
recipients a copy of this License along with the Program.
|
|
1956
|
+
|
|
1957
|
+
You may charge any price or no price for each copy that you convey,
|
|
1958
|
+
and you may offer support or warranty protection for a fee.
|
|
1959
|
+
|
|
1960
|
+
5. Conveying Modified Source Versions.
|
|
1961
|
+
|
|
1962
|
+
You may convey a work based on the Program, or the modifications to
|
|
1963
|
+
produce it from the Program, in the form of source code under the
|
|
1964
|
+
terms of section 4, provided that you also meet all of these conditions:
|
|
1965
|
+
|
|
1966
|
+
a) The work must carry prominent notices stating that you modified
|
|
1967
|
+
it, and giving a relevant date.
|
|
1968
|
+
|
|
1969
|
+
b) The work must carry prominent notices stating that it is
|
|
1970
|
+
released under this License and any conditions added under section
|
|
1971
|
+
7. This requirement modifies the requirement in section 4 to
|
|
1972
|
+
"keep intact all notices".
|
|
1973
|
+
|
|
1974
|
+
c) You must license the entire work, as a whole, under this
|
|
1975
|
+
License to anyone who comes into possession of a copy. This
|
|
1976
|
+
License will therefore apply, along with any applicable section 7
|
|
1977
|
+
additional terms, to the whole of the work, and all its parts,
|
|
1978
|
+
regardless of how they are packaged. This License gives no
|
|
1979
|
+
permission to license the work in any other way, but it does not
|
|
1980
|
+
invalidate such permission if you have separately received it.
|
|
1981
|
+
|
|
1982
|
+
d) If the work has interactive user interfaces, each must display
|
|
1983
|
+
Appropriate Legal Notices; however, if the Program has interactive
|
|
1984
|
+
interfaces that do not display Appropriate Legal Notices, your
|
|
1985
|
+
work need not make them do so.
|
|
1986
|
+
|
|
1987
|
+
A compilation of a covered work with other separate and independent
|
|
1988
|
+
works, which are not by their nature extensions of the covered work,
|
|
1989
|
+
and which are not combined with it such as to form a larger program,
|
|
1990
|
+
in or on a volume of a storage or distribution medium, is called an
|
|
1991
|
+
"aggregate" if the compilation and its resulting copyright are not
|
|
1992
|
+
used to limit the access or legal rights of the compilation's users
|
|
1993
|
+
beyond what the individual works permit. Inclusion of a covered work
|
|
1994
|
+
in an aggregate does not cause this License to apply to the other
|
|
1995
|
+
parts of the aggregate.
|
|
1996
|
+
|
|
1997
|
+
6. Conveying Non-Source Forms.
|
|
1998
|
+
|
|
1999
|
+
You may convey a covered work in object code form under the terms
|
|
2000
|
+
of sections 4 and 5, provided that you also convey the
|
|
2001
|
+
machine-readable Corresponding Source under the terms of this License,
|
|
2002
|
+
in one of these ways:
|
|
2003
|
+
|
|
2004
|
+
a) Convey the object code in, or embodied in, a physical product
|
|
2005
|
+
(including a physical distribution medium), accompanied by the
|
|
2006
|
+
Corresponding Source fixed on a durable physical medium
|
|
2007
|
+
customarily used for software interchange.
|
|
2008
|
+
|
|
2009
|
+
b) Convey the object code in, or embodied in, a physical product
|
|
2010
|
+
(including a physical distribution medium), accompanied by a
|
|
2011
|
+
written offer, valid for at least three years and valid for as
|
|
2012
|
+
long as you offer spare parts or customer support for that product
|
|
2013
|
+
model, to give anyone who possesses the object code either (1) a
|
|
2014
|
+
copy of the Corresponding Source for all the software in the
|
|
2015
|
+
product that is covered by this License, on a durable physical
|
|
2016
|
+
medium customarily used for software interchange, for a price no
|
|
2017
|
+
more than your reasonable cost of physically performing this
|
|
2018
|
+
conveying of source, or (2) access to copy the
|
|
2019
|
+
Corresponding Source from a network server at no charge.
|
|
2020
|
+
|
|
2021
|
+
c) Convey individual copies of the object code with a copy of the
|
|
2022
|
+
written offer to provide the Corresponding Source. This
|
|
2023
|
+
alternative is allowed only occasionally and noncommercially, and
|
|
2024
|
+
only if you received the object code with such an offer, in accord
|
|
2025
|
+
with subsection 6b.
|
|
2026
|
+
|
|
2027
|
+
d) Convey the object code by offering access from a designated
|
|
2028
|
+
place (gratis or for a charge), and offer equivalent access to the
|
|
2029
|
+
Corresponding Source in the same way through the same place at no
|
|
2030
|
+
further charge. You need not require recipients to copy the
|
|
2031
|
+
Corresponding Source along with the object code. If the place to
|
|
2032
|
+
copy the object code is a network server, the Corresponding Source
|
|
2033
|
+
may be on a different server (operated by you or a third party)
|
|
2034
|
+
that supports equivalent copying facilities, provided you maintain
|
|
2035
|
+
clear directions next to the object code saying where to find the
|
|
2036
|
+
Corresponding Source. Regardless of what server hosts the
|
|
2037
|
+
Corresponding Source, you remain obligated to ensure that it is
|
|
2038
|
+
available for as long as needed to satisfy these requirements.
|
|
2039
|
+
|
|
2040
|
+
e) Convey the object code using peer-to-peer transmission, provided
|
|
2041
|
+
you inform other peers where the object code and Corresponding
|
|
2042
|
+
Source of the work are being offered to the general public at no
|
|
2043
|
+
charge under subsection 6d.
|
|
2044
|
+
|
|
2045
|
+
A separable portion of the object code, whose source code is excluded
|
|
2046
|
+
from the Corresponding Source as a System Library, need not be
|
|
2047
|
+
included in conveying the object code work.
|
|
2048
|
+
|
|
2049
|
+
A "User Product" is either (1) a "consumer product", which means any
|
|
2050
|
+
tangible personal property which is normally used for personal, family,
|
|
2051
|
+
or household purposes, or (2) anything designed or sold for incorporation
|
|
2052
|
+
into a dwelling. In determining whether a product is a consumer product,
|
|
2053
|
+
doubtful cases shall be resolved in favor of coverage. For a particular
|
|
2054
|
+
product received by a particular user, "normally used" refers to a
|
|
2055
|
+
typical or common use of that class of product, regardless of the status
|
|
2056
|
+
of the particular user or of the way in which the particular user
|
|
2057
|
+
actually uses, or expects or is expected to use, the product. A product
|
|
2058
|
+
is a consumer product regardless of whether the product has substantial
|
|
2059
|
+
commercial, industrial or non-consumer uses, unless such uses represent
|
|
2060
|
+
the only significant mode of use of the product.
|
|
2061
|
+
|
|
2062
|
+
"Installation Information" for a User Product means any methods,
|
|
2063
|
+
procedures, authorization keys, or other information required to install
|
|
2064
|
+
and execute modified versions of a covered work in that User Product from
|
|
2065
|
+
a modified version of its Corresponding Source. The information must
|
|
2066
|
+
suffice to ensure that the continued functioning of the modified object
|
|
2067
|
+
code is in no case prevented or interfered with solely because
|
|
2068
|
+
modification has been made.
|
|
2069
|
+
|
|
2070
|
+
If you convey an object code work under this section in, or with, or
|
|
2071
|
+
specifically for use in, a User Product, and the conveying occurs as
|
|
2072
|
+
part of a transaction in which the right of possession and use of the
|
|
2073
|
+
User Product is transferred to the recipient in perpetuity or for a
|
|
2074
|
+
fixed term (regardless of how the transaction is characterized), the
|
|
2075
|
+
Corresponding Source conveyed under this section must be accompanied
|
|
2076
|
+
by the Installation Information. But this requirement does not apply
|
|
2077
|
+
if neither you nor any third party retains the ability to install
|
|
2078
|
+
modified object code on the User Product (for example, the work has
|
|
2079
|
+
been installed in ROM).
|
|
2080
|
+
|
|
2081
|
+
The requirement to provide Installation Information does not include a
|
|
2082
|
+
requirement to continue to provide support service, warranty, or updates
|
|
2083
|
+
for a work that has been modified or installed by the recipient, or for
|
|
2084
|
+
the User Product in which it has been modified or installed. Access to a
|
|
2085
|
+
network may be denied when the modification itself materially and
|
|
2086
|
+
adversely affects the operation of the network or violates the rules and
|
|
2087
|
+
protocols for communication across the network.
|
|
2088
|
+
|
|
2089
|
+
Corresponding Source conveyed, and Installation Information provided,
|
|
2090
|
+
in accord with this section must be in a format that is publicly
|
|
2091
|
+
documented (and with an implementation available to the public in
|
|
2092
|
+
source code form), and must require no special password or key for
|
|
2093
|
+
unpacking, reading or copying.
|
|
2094
|
+
|
|
2095
|
+
7. Additional Terms.
|
|
2096
|
+
|
|
2097
|
+
"Additional permissions" are terms that supplement the terms of this
|
|
2098
|
+
License by making exceptions from one or more of its conditions.
|
|
2099
|
+
Additional permissions that are applicable to the entire Program shall
|
|
2100
|
+
be treated as though they were included in this License, to the extent
|
|
2101
|
+
that they are valid under applicable law. If additional permissions
|
|
2102
|
+
apply only to part of the Program, that part may be used separately
|
|
2103
|
+
under those permissions, but the entire Program remains governed by
|
|
2104
|
+
this License without regard to the additional permissions.
|
|
2105
|
+
|
|
2106
|
+
When you convey a copy of a covered work, you may at your option
|
|
2107
|
+
remove any additional permissions from that copy, or from any part of
|
|
2108
|
+
it. (Additional permissions may be written to require their own
|
|
2109
|
+
removal in certain cases when you modify the work.) You may place
|
|
2110
|
+
additional permissions on material, added by you to a covered work,
|
|
2111
|
+
for which you have or can give appropriate copyright permission.
|
|
2112
|
+
|
|
2113
|
+
Notwithstanding any other provision of this License, for material you
|
|
2114
|
+
add to a covered work, you may (if authorized by the copyright holders of
|
|
2115
|
+
that material) supplement the terms of this License with terms:
|
|
2116
|
+
|
|
2117
|
+
a) Disclaiming warranty or limiting liability differently from the
|
|
2118
|
+
terms of sections 15 and 16 of this License; or
|
|
2119
|
+
|
|
2120
|
+
b) Requiring preservation of specified reasonable legal notices or
|
|
2121
|
+
author attributions in that material or in the Appropriate Legal
|
|
2122
|
+
Notices displayed by works containing it; or
|
|
2123
|
+
|
|
2124
|
+
c) Prohibiting misrepresentation of the origin of that material, or
|
|
2125
|
+
requiring that modified versions of such material be marked in
|
|
2126
|
+
reasonable ways as different from the original version; or
|
|
2127
|
+
|
|
2128
|
+
d) Limiting the use for publicity purposes of names of licensors or
|
|
2129
|
+
authors of the material; or
|
|
2130
|
+
|
|
2131
|
+
e) Declining to grant rights under trademark law for use of some
|
|
2132
|
+
trade names, trademarks, or service marks; or
|
|
2133
|
+
|
|
2134
|
+
f) Requiring indemnification of licensors and authors of that
|
|
2135
|
+
material by anyone who conveys the material (or modified versions of
|
|
2136
|
+
it) with contractual assumptions of liability to the recipient, for
|
|
2137
|
+
any liability that these contractual assumptions directly impose on
|
|
2138
|
+
those licensors and authors.
|
|
2139
|
+
|
|
2140
|
+
All other non-permissive additional terms are considered "further
|
|
2141
|
+
restrictions" within the meaning of section 10. If the Program as you
|
|
2142
|
+
received it, or any part of it, contains a notice stating that it is
|
|
2143
|
+
governed by this License along with a term that is a further
|
|
2144
|
+
restriction, you may remove that term. If a license document contains
|
|
2145
|
+
a further restriction but permits relicensing or conveying under this
|
|
2146
|
+
License, you may add to a covered work material governed by the terms
|
|
2147
|
+
of that license document, provided that the further restriction does
|
|
2148
|
+
not survive such relicensing or conveying.
|
|
2149
|
+
|
|
2150
|
+
If you add terms to a covered work in accord with this section, you
|
|
2151
|
+
must place, in the relevant source files, a statement of the
|
|
2152
|
+
additional terms that apply to those files, or a notice indicating
|
|
2153
|
+
where to find the applicable terms.
|
|
2154
|
+
|
|
2155
|
+
Additional terms, permissive or non-permissive, may be stated in the
|
|
2156
|
+
form of a separately written license, or stated as exceptions;
|
|
2157
|
+
the above requirements apply either way.
|
|
2158
|
+
|
|
2159
|
+
8. Termination.
|
|
2160
|
+
|
|
2161
|
+
You may not propagate or modify a covered work except as expressly
|
|
2162
|
+
provided under this License. Any attempt otherwise to propagate or
|
|
2163
|
+
modify it is void, and will automatically terminate your rights under
|
|
2164
|
+
this License (including any patent licenses granted under the third
|
|
2165
|
+
paragraph of section 11).
|
|
2166
|
+
|
|
2167
|
+
However, if you cease all violation of this License, then your
|
|
2168
|
+
license from a particular copyright holder is reinstated (a)
|
|
2169
|
+
provisionally, unless and until the copyright holder explicitly and
|
|
2170
|
+
finally terminates your license, and (b) permanently, if the copyright
|
|
2171
|
+
holder fails to notify you of the violation by some reasonable means
|
|
2172
|
+
prior to 60 days after the cessation.
|
|
2173
|
+
|
|
2174
|
+
Moreover, your license from a particular copyright holder is
|
|
2175
|
+
reinstated permanently if the copyright holder notifies you of the
|
|
2176
|
+
violation by some reasonable means, this is the first time you have
|
|
2177
|
+
received notice of violation of this License (for any work) from that
|
|
2178
|
+
copyright holder, and you cure the violation prior to 30 days after
|
|
2179
|
+
your receipt of the notice.
|
|
2180
|
+
|
|
2181
|
+
Termination of your rights under this section does not terminate the
|
|
2182
|
+
licenses of parties who have received copies or rights from you under
|
|
2183
|
+
this License. If your rights have been terminated and not permanently
|
|
2184
|
+
reinstated, you do not qualify to receive new licenses for the same
|
|
2185
|
+
material under section 10.
|
|
2186
|
+
|
|
2187
|
+
9. Acceptance Not Required for Having Copies.
|
|
2188
|
+
|
|
2189
|
+
You are not required to accept this License in order to receive or
|
|
2190
|
+
run a copy of the Program. Ancillary propagation of a covered work
|
|
2191
|
+
occurring solely as a consequence of using peer-to-peer transmission
|
|
2192
|
+
to receive a copy likewise does not require acceptance. However,
|
|
2193
|
+
nothing other than this License grants you permission to propagate or
|
|
2194
|
+
modify any covered work. These actions infringe copyright if you do
|
|
2195
|
+
not accept this License. Therefore, by modifying or propagating a
|
|
2196
|
+
covered work, you indicate your acceptance of this License to do so.
|
|
2197
|
+
|
|
2198
|
+
10. Automatic Licensing of Downstream Recipients.
|
|
2199
|
+
|
|
2200
|
+
Each time you convey a covered work, the recipient automatically
|
|
2201
|
+
receives a license from the original licensors, to run, modify and
|
|
2202
|
+
propagate that work, subject to this License. You are not responsible
|
|
2203
|
+
for enforcing compliance by third parties with this License.
|
|
2204
|
+
|
|
2205
|
+
An "entity transaction" is a transaction transferring control of an
|
|
2206
|
+
organization, or substantially all assets of one, or subdividing an
|
|
2207
|
+
organization, or merging organizations. If propagation of a covered
|
|
2208
|
+
work results from an entity transaction, each party to that
|
|
2209
|
+
transaction who receives a copy of the work also receives whatever
|
|
2210
|
+
licenses to the work the party's predecessor in interest had or could
|
|
2211
|
+
give under the previous paragraph, plus a right to possession of the
|
|
2212
|
+
Corresponding Source of the work from the predecessor in interest, if
|
|
2213
|
+
the predecessor has it or can get it with reasonable efforts.
|
|
2214
|
+
|
|
2215
|
+
You may not impose any further restrictions on the exercise of the
|
|
2216
|
+
rights granted or affirmed under this License. For example, you may
|
|
2217
|
+
not impose a license fee, royalty, or other charge for exercise of
|
|
2218
|
+
rights granted under this License, and you may not initiate litigation
|
|
2219
|
+
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
|
2220
|
+
any patent claim is infringed by making, using, selling, offering for
|
|
2221
|
+
sale, or importing the Program or any portion of it.
|
|
2222
|
+
|
|
2223
|
+
11. Patents.
|
|
2224
|
+
|
|
2225
|
+
A "contributor" is a copyright holder who authorizes use under this
|
|
2226
|
+
License of the Program or a work on which the Program is based. The
|
|
2227
|
+
work thus licensed is called the contributor's "contributor version".
|
|
2228
|
+
|
|
2229
|
+
A contributor's "essential patent claims" are all patent claims
|
|
2230
|
+
owned or controlled by the contributor, whether already acquired or
|
|
2231
|
+
hereafter acquired, that would be infringed by some manner, permitted
|
|
2232
|
+
by this License, of making, using, or selling its contributor version,
|
|
2233
|
+
but do not include claims that would be infringed only as a
|
|
2234
|
+
consequence of further modification of the contributor version. For
|
|
2235
|
+
purposes of this definition, "control" includes the right to grant
|
|
2236
|
+
patent sublicenses in a manner consistent with the requirements of
|
|
2237
|
+
this License.
|
|
2238
|
+
|
|
2239
|
+
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
|
2240
|
+
patent license under the contributor's essential patent claims, to
|
|
2241
|
+
make, use, sell, offer for sale, import and otherwise run, modify and
|
|
2242
|
+
propagate the contents of its contributor version.
|
|
2243
|
+
|
|
2244
|
+
In the following three paragraphs, a "patent license" is any express
|
|
2245
|
+
agreement or commitment, however denominated, not to enforce a patent
|
|
2246
|
+
(such as an express permission to practice a patent or covenant not to
|
|
2247
|
+
sue for patent infringement). To "grant" such a patent license to a
|
|
2248
|
+
party means to make such an agreement or commitment not to enforce a
|
|
2249
|
+
patent against the party.
|
|
2250
|
+
|
|
2251
|
+
If you convey a covered work, knowingly relying on a patent license,
|
|
2252
|
+
and the Corresponding Source of the work is not available for anyone
|
|
2253
|
+
to copy, free of charge and under the terms of this License, through a
|
|
2254
|
+
publicly available network server or other readily accessible means,
|
|
2255
|
+
then you must either (1) cause the Corresponding Source to be so
|
|
2256
|
+
available, or (2) arrange to deprive yourself of the benefit of the
|
|
2257
|
+
patent license for this particular work, or (3) arrange, in a manner
|
|
2258
|
+
consistent with the requirements of this License, to extend the patent
|
|
2259
|
+
license to downstream recipients. "Knowingly relying" means you have
|
|
2260
|
+
actual knowledge that, but for the patent license, your conveying the
|
|
2261
|
+
covered work in a country, or your recipient's use of the covered work
|
|
2262
|
+
in a country, would infringe one or more identifiable patents in that
|
|
2263
|
+
country that you have reason to believe are valid.
|
|
2264
|
+
|
|
2265
|
+
If, pursuant to or in connection with a single transaction or
|
|
2266
|
+
arrangement, you convey, or propagate by procuring conveyance of, a
|
|
2267
|
+
covered work, and grant a patent license to some of the parties
|
|
2268
|
+
receiving the covered work authorizing them to use, propagate, modify
|
|
2269
|
+
or convey a specific copy of the covered work, then the patent license
|
|
2270
|
+
you grant is automatically extended to all recipients of the covered
|
|
2271
|
+
work and works based on it.
|
|
2272
|
+
|
|
2273
|
+
A patent license is "discriminatory" if it does not include within
|
|
2274
|
+
the scope of its coverage, prohibits the exercise of, or is
|
|
2275
|
+
conditioned on the non-exercise of one or more of the rights that are
|
|
2276
|
+
specifically granted under this License. You may not convey a covered
|
|
2277
|
+
work if you are a party to an arrangement with a third party that is
|
|
2278
|
+
in the business of distributing software, under which you make payment
|
|
2279
|
+
to the third party based on the extent of your activity of conveying
|
|
2280
|
+
the work, and under which the third party grants, to any of the
|
|
2281
|
+
parties who would receive the covered work from you, a discriminatory
|
|
2282
|
+
patent license (a) in connection with copies of the covered work
|
|
2283
|
+
conveyed by you (or copies made from those copies), or (b) primarily
|
|
2284
|
+
for and in connection with specific products or compilations that
|
|
2285
|
+
contain the covered work, unless you entered into that arrangement,
|
|
2286
|
+
or that patent license was granted, prior to 28 March 2007.
|
|
2287
|
+
|
|
2288
|
+
Nothing in this License shall be construed as excluding or limiting
|
|
2289
|
+
any implied license or other defenses to infringement that may
|
|
2290
|
+
otherwise be available to you under applicable patent law.
|
|
2291
|
+
|
|
2292
|
+
12. No Surrender of Others' Freedom.
|
|
2293
|
+
|
|
2294
|
+
If conditions are imposed on you (whether by court order, agreement or
|
|
2295
|
+
otherwise) that contradict the conditions of this License, they do not
|
|
2296
|
+
excuse you from the conditions of this License. If you cannot convey a
|
|
2297
|
+
covered work so as to satisfy simultaneously your obligations under this
|
|
2298
|
+
License and any other pertinent obligations, then as a consequence you may
|
|
2299
|
+
not convey it at all. For example, if you agree to terms that obligate you
|
|
2300
|
+
to collect a royalty for further conveying from those to whom you convey
|
|
2301
|
+
the Program, the only way you could satisfy both those terms and this
|
|
2302
|
+
License would be to refrain entirely from conveying the Program.
|
|
2303
|
+
|
|
2304
|
+
13. Remote Network Interaction; Use with the GNU General Public License.
|
|
2305
|
+
|
|
2306
|
+
Notwithstanding any other provision of this License, if you modify the
|
|
2307
|
+
Program, your modified version must prominently offer all users
|
|
2308
|
+
interacting with it remotely through a computer network (if your version
|
|
2309
|
+
supports such interaction) an opportunity to receive the Corresponding
|
|
2310
|
+
Source of your version by providing access to the Corresponding Source
|
|
2311
|
+
from a network server at no charge, through some standard or customary
|
|
2312
|
+
means of facilitating copying of software. This Corresponding Source
|
|
2313
|
+
shall include the Corresponding Source for any work covered by version 3
|
|
2314
|
+
of the GNU General Public License that is incorporated pursuant to the
|
|
2315
|
+
following paragraph.
|
|
2316
|
+
|
|
2317
|
+
Notwithstanding any other provision of this License, you have
|
|
2318
|
+
permission to link or combine any covered work with a work licensed
|
|
2319
|
+
under version 3 of the GNU General Public License into a single
|
|
2320
|
+
combined work, and to convey the resulting work. The terms of this
|
|
2321
|
+
License will continue to apply to the part which is the covered work,
|
|
2322
|
+
but the work with which it is combined will remain governed by version
|
|
2323
|
+
3 of the GNU General Public License.
|
|
2324
|
+
|
|
2325
|
+
14. Revised Versions of this License.
|
|
2326
|
+
|
|
2327
|
+
The Free Software Foundation may publish revised and/or new versions of
|
|
2328
|
+
the GNU Affero General Public License from time to time. Such new versions
|
|
2329
|
+
will be similar in spirit to the present version, but may differ in detail to
|
|
2330
|
+
address new problems or concerns.
|
|
2331
|
+
|
|
2332
|
+
Each version is given a distinguishing version number. If the
|
|
2333
|
+
Program specifies that a certain numbered version of the GNU Affero General
|
|
2334
|
+
Public License "or any later version" applies to it, you have the
|
|
2335
|
+
option of following the terms and conditions either of that numbered
|
|
2336
|
+
version or of any later version published by the Free Software
|
|
2337
|
+
Foundation. If the Program does not specify a version number of the
|
|
2338
|
+
GNU Affero General Public License, you may choose any version ever published
|
|
2339
|
+
by the Free Software Foundation.
|
|
2340
|
+
|
|
2341
|
+
If the Program specifies that a proxy can decide which future
|
|
2342
|
+
versions of the GNU Affero General Public License can be used, that proxy's
|
|
2343
|
+
public statement of acceptance of a version permanently authorizes you
|
|
2344
|
+
to choose that version for the Program.
|
|
2345
|
+
|
|
2346
|
+
Later license versions may give you additional or different
|
|
2347
|
+
permissions. However, no additional obligations are imposed on any
|
|
2348
|
+
author or copyright holder as a result of your choosing to follow a
|
|
2349
|
+
later version.
|
|
2350
|
+
|
|
2351
|
+
15. Disclaimer of Warranty.
|
|
2352
|
+
|
|
2353
|
+
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
|
2354
|
+
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
|
2355
|
+
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
|
2356
|
+
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
|
2357
|
+
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
2358
|
+
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
|
2359
|
+
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
|
2360
|
+
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
|
2361
|
+
|
|
2362
|
+
16. Limitation of Liability.
|
|
2363
|
+
|
|
2364
|
+
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
|
2365
|
+
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
|
2366
|
+
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
|
2367
|
+
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
|
2368
|
+
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
|
2369
|
+
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
|
2370
|
+
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
|
2371
|
+
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
|
2372
|
+
SUCH DAMAGES.
|
|
2373
|
+
|
|
2374
|
+
17. Interpretation of Sections 15 and 16.
|
|
2375
|
+
|
|
2376
|
+
If the disclaimer of warranty and limitation of liability provided
|
|
2377
|
+
above cannot be given local legal effect according to their terms,
|
|
2378
|
+
reviewing courts shall apply local law that most closely approximates
|
|
2379
|
+
an absolute waiver of all civil liability in connection with the
|
|
2380
|
+
Program, unless a warranty or assumption of liability accompanies a
|
|
2381
|
+
copy of the Program in return for a fee.
|
|
2382
|
+
|
|
2383
|
+
END OF TERMS AND CONDITIONS
|
|
2384
|
+
|
|
2385
|
+
How to Apply These Terms to Your New Programs
|
|
2386
|
+
|
|
2387
|
+
If you develop a new program, and you want it to be of the greatest
|
|
2388
|
+
possible use to the public, the best way to achieve this is to make it
|
|
2389
|
+
free software which everyone can redistribute and change under these terms.
|
|
2390
|
+
|
|
2391
|
+
To do so, attach the following notices to the program. It is safest
|
|
2392
|
+
to attach them to the start of each source file to most effectively
|
|
2393
|
+
state the exclusion of warranty; and each file should have at least
|
|
2394
|
+
the "copyright" line and a pointer to where the full notice is found.
|
|
2395
|
+
|
|
2396
|
+
<one line to give the program's name and a brief idea of what it does.>
|
|
2397
|
+
Copyright (C) <year> <name of author>
|
|
2398
|
+
|
|
2399
|
+
This program is free software: you can redistribute it and/or modify
|
|
2400
|
+
it under the terms of the GNU Affero General Public License as published
|
|
2401
|
+
by the Free Software Foundation, either version 3 of the License, or
|
|
2402
|
+
(at your option) any later version.
|
|
2403
|
+
|
|
2404
|
+
This program is distributed in the hope that it will be useful,
|
|
2405
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
2406
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
2407
|
+
GNU Affero General Public License for more details.
|
|
2408
|
+
|
|
2409
|
+
You should have received a copy of the GNU Affero General Public License
|
|
2410
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
2411
|
+
|
|
2412
|
+
Also add information on how to contact you by electronic and paper mail.
|
|
2413
|
+
|
|
2414
|
+
If your software can interact with users remotely through a computer
|
|
2415
|
+
network, you should also make sure that it provides a way for users to
|
|
2416
|
+
get its source. For example, if your program is a web application, its
|
|
2417
|
+
interface could display a "Source" link that leads users to an archive
|
|
2418
|
+
of the code. There are many ways you could offer source, and different
|
|
2419
|
+
solutions will be better for different programs; see section 13 for the
|
|
2420
|
+
specific requirements.
|
|
2421
|
+
|
|
2422
|
+
You should also get your employer (if you work as a programmer) or school,
|
|
2423
|
+
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
|
2424
|
+
For more information on this, and how to apply and follow the GNU AGPL, see
|
|
2425
|
+
<https://www.gnu.org/licenses/>.
|
|
2426
|
+
`;
|
|
2427
|
+
//#endregion
|
|
2428
|
+
//#region src/templates/boilerplate/main.tsx.ts
|
|
2429
|
+
const mainTsxTemplate = tsx`
|
|
2430
|
+
import { startConnect } from "@powerhousedao/connect";
|
|
2431
|
+
import * as localPackage from "./index.js";
|
|
2432
|
+
|
|
2433
|
+
startConnect(localPackage);
|
|
2434
|
+
`.raw;
|
|
2435
|
+
//#endregion
|
|
2436
|
+
//#region src/templates/boilerplate/mcp.json.ts
|
|
2437
|
+
const mcpTemplate = json`
|
|
2438
|
+
{
|
|
2439
|
+
"mcpServers": {
|
|
2440
|
+
"reactor-mcp": {
|
|
2441
|
+
"type": "http",
|
|
2442
|
+
"url": "http://localhost:4001/mcp"
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
`.raw;
|
|
2447
|
+
//#endregion
|
|
2448
|
+
//#region src/templates/boilerplate/npmrc.ts
|
|
2449
|
+
const npmrcTemplate = `@jsr:registry=https://npm.jsr.io`;
|
|
2450
|
+
//#endregion
|
|
2451
|
+
//#region src/templates/boilerplate/package.json.ts
|
|
2452
|
+
const exportsTemplate = json`
|
|
2453
|
+
".": {
|
|
2454
|
+
"types": "./dist/index.d.ts",
|
|
2455
|
+
"browser": "./dist/browser/index.js",
|
|
2456
|
+
"node": "./dist/node/index.mjs"
|
|
2457
|
+
},
|
|
2458
|
+
"./document-models": {
|
|
2459
|
+
"types": "./dist/document-models/index.d.ts",
|
|
2460
|
+
"browser": "./dist/browser/document-models/index.js",
|
|
2461
|
+
"node": "./dist/node/document-models/index.mjs"
|
|
2462
|
+
},
|
|
2463
|
+
"./document-models/*": {
|
|
2464
|
+
"types": "./dist/document-models/*/index.d.ts",
|
|
2465
|
+
"browser": "./dist/browser/document-models/*/index.js",
|
|
2466
|
+
"node": "./dist/node/document-models/*/index.mjs"
|
|
2467
|
+
},
|
|
2468
|
+
"./editors": {
|
|
2469
|
+
"types": "./dist/editors/index.d.ts",
|
|
2470
|
+
"browser": "./dist/browser/editors/index.js",
|
|
2471
|
+
"node": "./dist/node/editors/index.mjs"
|
|
2472
|
+
},
|
|
2473
|
+
"./editors/*": {
|
|
2474
|
+
"types": "./dist/editors/*/index.d.ts",
|
|
2475
|
+
"browser": "./dist/browser/editors/*/index.js",
|
|
2476
|
+
"node": "./dist/node/editors/*/index.mjs"
|
|
2477
|
+
},
|
|
2478
|
+
"./subgraphs": {
|
|
2479
|
+
"types": "./dist/subgraphs/index.d.ts",
|
|
2480
|
+
"browser": "./dist/browser/subgraphs/index.js",
|
|
2481
|
+
"node": "./dist/node/subgraphs/index.mjs"
|
|
2482
|
+
},
|
|
2483
|
+
"./processors": {
|
|
2484
|
+
"types": "./dist/processors/index.d.ts",
|
|
2485
|
+
"browser": "./dist/browser/processors/index.js",
|
|
2486
|
+
"node": "./dist/node/processors/index.mjs"
|
|
2487
|
+
},
|
|
2488
|
+
"./manifest": "./dist/powerhouse.manifest.json",
|
|
2489
|
+
"./style.css": "./dist/style.css"
|
|
2490
|
+
`.raw;
|
|
2491
|
+
const scriptsTemplate = json`
|
|
2492
|
+
"test": "vitest run",
|
|
2493
|
+
"test:watch": "vitest",
|
|
2494
|
+
"lint": "eslint --config eslint.config.js --cache --cache-strategy content",
|
|
2495
|
+
"lint:fix": "npm run lint -- --fix",
|
|
2496
|
+
"tsc": "tsc",
|
|
2497
|
+
"tsc:watch": "tsc --watch",
|
|
2498
|
+
"check-circular-imports": "npx dpdm -T ./index.ts",
|
|
2499
|
+
"generate": "ph-cli generate",
|
|
2500
|
+
"connect": "ph-cli connect",
|
|
2501
|
+
"build": "ph-cli build",
|
|
2502
|
+
"reactor": "ph-cli reactor",
|
|
2503
|
+
"service": "ph-cli service",
|
|
2504
|
+
"vetra": "ph-cli vetra",
|
|
2505
|
+
"service-startup": "bash ./node_modules/@powerhousedao/ph-cli/dist/scripts/service-startup.sh",
|
|
2506
|
+
"service-unstartup": "bash ./node_modules/@powerhousedao/ph-cli/dist/scripts/service-unstartup.sh"
|
|
2507
|
+
`.raw;
|
|
2508
|
+
const dependenciesTemplate = (versionedDependencies) => json`
|
|
2509
|
+
${versionedDependencies.join(",\n")},
|
|
2510
|
+
"@powerhousedao/document-engineering": "1.40.1",
|
|
2511
|
+
"graphql": "^16.10.0",
|
|
2512
|
+
"graphql-tag": "^2.12.6",
|
|
2513
|
+
"zod": "^4.3.5",
|
|
2514
|
+
"react": "^19.2.3",
|
|
2515
|
+
"react-dom": "^19.2.3"
|
|
2516
|
+
`.raw;
|
|
2517
|
+
const devDependenciesTemplate = (versionedDevDependencies) => json`
|
|
2518
|
+
${versionedDevDependencies.join(",\n")},
|
|
2519
|
+
"@eslint/js": "^9.38.0",
|
|
2520
|
+
"@tailwindcss/cli": "^4.1.18",
|
|
2521
|
+
"@types/node": "^24.9.2",
|
|
2522
|
+
"@types/react": "^19.2.3",
|
|
2523
|
+
"eslint": "^9.38.0",
|
|
2524
|
+
"eslint-plugin-react": "^7.37.5",
|
|
2525
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
2526
|
+
"eslint-config-prettier": "^10.1.8",
|
|
2527
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
2528
|
+
"globals": "^16.4.0",
|
|
2529
|
+
"tailwindcss": "^4.1.16",
|
|
2530
|
+
"typescript": "^5.9.3",
|
|
2531
|
+
"typescript-eslint": "^8.46.2",
|
|
2532
|
+
"vitest": "4.1.1",
|
|
2533
|
+
"@vitejs/plugin-react": "6.0.1",
|
|
2534
|
+
"vite-tsconfig-paths": "6.1.1"
|
|
2535
|
+
`.raw;
|
|
2536
|
+
const packageJsonTemplate = (projectName, versionedDependencies, versionedDevDependencies) => json`
|
|
2537
|
+
{
|
|
2538
|
+
"name": "${projectName}",
|
|
2539
|
+
"version": "1.0.0",
|
|
2540
|
+
"license": "AGPL-3.0-only",
|
|
2541
|
+
"type": "module",
|
|
2542
|
+
"files": [
|
|
2543
|
+
"/dist"
|
|
2544
|
+
],
|
|
2545
|
+
"exports": {
|
|
2546
|
+
${exportsTemplate}
|
|
2547
|
+
},
|
|
2548
|
+
"scripts": {
|
|
2549
|
+
${scriptsTemplate}
|
|
2550
|
+
},
|
|
2551
|
+
"dependencies": {
|
|
2552
|
+
${dependenciesTemplate(versionedDependencies)}
|
|
2553
|
+
},
|
|
2554
|
+
"devDependencies": {
|
|
2555
|
+
${devDependenciesTemplate(versionedDevDependencies)}
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
`.raw;
|
|
2559
|
+
//#endregion
|
|
2560
|
+
//#region src/templates/boilerplate/package.json.legacy.ts
|
|
2561
|
+
const packageJsonScriptsTemplate = {
|
|
2562
|
+
build: "npm run tsc && npm run tailwind",
|
|
2563
|
+
test: "vitest run",
|
|
2564
|
+
"test:watch": "vitest",
|
|
2565
|
+
lint: "eslint --config eslint.config.js --cache --cache-strategy content",
|
|
2566
|
+
"lint:fix": "npm run lint -- --fix",
|
|
2567
|
+
tsc: "tsc",
|
|
2568
|
+
"tsc:watch": "tsc --watch",
|
|
2569
|
+
tailwind: "pnpm exec tailwindcss -i ./style.css -o ./dist/style.css",
|
|
2570
|
+
prepublishOnly: "npm run build",
|
|
2571
|
+
"check-circular-imports": "npx dpdm -T ./index.ts",
|
|
2572
|
+
generate: "ph-cli generate",
|
|
2573
|
+
connect: "ph-cli connect",
|
|
2574
|
+
reactor: "ph-cli reactor",
|
|
2575
|
+
service: "ph-cli service",
|
|
2576
|
+
vetra: "ph-cli vetra",
|
|
2577
|
+
migrate: "ph-cli migrate",
|
|
2578
|
+
"service-startup": "bash ./node_modules/@powerhousedao/ph-cli/dist/scripts/service-startup.sh",
|
|
2579
|
+
"service-unstartup": "bash ./node_modules/@powerhousedao/ph-cli/dist/scripts/service-unstartup.sh"
|
|
2580
|
+
};
|
|
2581
|
+
const packageJsonExportsTemplate = {
|
|
2582
|
+
".": {
|
|
2583
|
+
types: "./dist/index.d.ts",
|
|
2584
|
+
default: "./dist/index.js"
|
|
2585
|
+
},
|
|
2586
|
+
"./document-models": {
|
|
2587
|
+
types: "./dist/document-models/index.d.ts",
|
|
2588
|
+
default: "./dist/document-models/index.js"
|
|
2589
|
+
},
|
|
2590
|
+
"./editors": {
|
|
2591
|
+
types: "./dist/editors/index.d.ts",
|
|
2592
|
+
default: "./dist/editors/index.js"
|
|
2593
|
+
},
|
|
2594
|
+
"./document-models/*": {
|
|
2595
|
+
types: "./dist/document-models/*/index.d.ts",
|
|
2596
|
+
default: "./dist/document-models/*/index.js"
|
|
2597
|
+
},
|
|
2598
|
+
"./editors/*": {
|
|
2599
|
+
types: "./dist/editors/*/index.d.ts",
|
|
2600
|
+
default: "./dist/editors/*/index.js"
|
|
2601
|
+
},
|
|
2602
|
+
"./subgraphs": {
|
|
2603
|
+
types: "./dist/subgraphs/index.d.ts",
|
|
2604
|
+
default: "./dist/subgraphs/index.js"
|
|
2605
|
+
},
|
|
2606
|
+
"./processors": {
|
|
2607
|
+
types: "./dist/processors/index.d.ts",
|
|
2608
|
+
default: "./dist/processors/index.js"
|
|
2609
|
+
},
|
|
2610
|
+
"./manifest": { default: "./dist/powerhouse.manifest.json" },
|
|
2611
|
+
"./style.css": "./dist/style.css"
|
|
2612
|
+
};
|
|
2613
|
+
//#endregion
|
|
2614
|
+
//#region src/templates/boilerplate/powerhouse.config.json.ts
|
|
2615
|
+
function makeVetraConfigField(vetraDriveUrl) {
|
|
2616
|
+
if (!vetraDriveUrl) return "";
|
|
2617
|
+
return json`
|
|
2618
|
+
,
|
|
2619
|
+
"vetra": {
|
|
2620
|
+
"driveId": "${vetraDriveUrl.split("/").pop() ?? ""}",
|
|
2621
|
+
"driveUrl": "${vetraDriveUrl}"
|
|
2622
|
+
}
|
|
2623
|
+
`.raw;
|
|
2624
|
+
}
|
|
2625
|
+
async function buildPowerhouseConfigTemplate(args) {
|
|
2626
|
+
return json`
|
|
2627
|
+
{
|
|
2628
|
+
"documentModelsDir": "./document-models",
|
|
2629
|
+
"editorsDir": "./editors",
|
|
2630
|
+
"processorsDir": "./processors",
|
|
2631
|
+
"subgraphsDir": "./subgraphs",
|
|
2632
|
+
"studio": {
|
|
2633
|
+
"port": 3000
|
|
2634
|
+
},
|
|
2635
|
+
"reactor": {
|
|
2636
|
+
"port": 4001
|
|
2637
|
+
},
|
|
2638
|
+
"packages": [
|
|
2639
|
+
]${makeVetraConfigField(args.remoteDrive)}
|
|
2640
|
+
}
|
|
2641
|
+
`.raw;
|
|
2642
|
+
}
|
|
2643
|
+
//#endregion
|
|
2644
|
+
//#region src/templates/boilerplate/powerhouse.manifest.json.ts
|
|
2645
|
+
const powerhouseManifestTemplate = (projectName) => json`
|
|
2646
|
+
{
|
|
2647
|
+
"name": "${projectName}",
|
|
2648
|
+
"description": "",
|
|
2649
|
+
"category": "",
|
|
2650
|
+
"publisher": {
|
|
2651
|
+
"name": "",
|
|
2652
|
+
"url": ""
|
|
2653
|
+
},
|
|
2654
|
+
"documentModels": [],
|
|
2655
|
+
"editors": [],
|
|
2656
|
+
"apps": [],
|
|
2657
|
+
"subgraphs": [],
|
|
2658
|
+
"importScripts": []
|
|
2659
|
+
}
|
|
2660
|
+
|
|
2661
|
+
`.raw;
|
|
2662
|
+
//#endregion
|
|
2663
|
+
//#region src/templates/boilerplate/README.md.ts
|
|
2664
|
+
const readmeTemplate = md`
|
|
2665
|
+
# Document Model Boilerplate
|
|
2666
|
+
|
|
2667
|
+
This Document Model Boilerplate provides code generation for scaffolding editors and models.
|
|
2668
|
+
It ensures compatibility with host applications like Connect and the Reactors for seamless document model and editor integration.
|
|
2669
|
+
|
|
2670
|
+
## Standard Document Model Workflow with help of the boilerplate.
|
|
2671
|
+
|
|
2672
|
+
This tutorial will guide you through the process of creating a new document model using the Document Model Editor in the Connect app.
|
|
2673
|
+
|
|
2674
|
+
<details>
|
|
2675
|
+
<summary>Available NPM commands</summary>
|
|
2676
|
+
|
|
2677
|
+
- \`generate\`: Updates the generated code according to the JSON spec and GraphQL schema of your document model, made in Connect.
|
|
2678
|
+
- \`lint\`: Checks for errors with ESLint and TypeScript checking.
|
|
2679
|
+
- \`format\`: Formats the code using Prettier.
|
|
2680
|
+
- \`build\`: Builds the library project using Vite.
|
|
2681
|
+
- \`storybook\`: Starts Storybook in development mode.
|
|
2682
|
+
- \`build-storybook\`: Builds Storybook.
|
|
2683
|
+
- \`test\`: Runs Jest for testing.
|
|
2684
|
+
|
|
2685
|
+
</details>
|
|
2686
|
+
|
|
2687
|
+
### 1. Defining Your Document Model GraphQL Schema
|
|
2688
|
+
|
|
2689
|
+
Start by creating your own 'Powerhouse Project' (Document model + editor).
|
|
2690
|
+
|
|
2691
|
+
Step 1: Run the following command to set up your project inside this directory:
|
|
2692
|
+
|
|
2693
|
+
~~~bash
|
|
2694
|
+
npm create document-model-lib
|
|
2695
|
+
~~~
|
|
2696
|
+
|
|
2697
|
+
Step 2: Use the Document Model Editor in the Connect app
|
|
2698
|
+
|
|
2699
|
+
The following command gives you access to all the powerhouse CLI tools available, install it globally if you are a poweruser.
|
|
2700
|
+
|
|
2701
|
+
~~~bash
|
|
2702
|
+
npm install ph-cmd
|
|
2703
|
+
~~~
|
|
2704
|
+
|
|
2705
|
+
Now you are able to launch Connect in Studio Mode (Locally):
|
|
2706
|
+
|
|
2707
|
+
~~~bash
|
|
2708
|
+
npm run connect
|
|
2709
|
+
~~~
|
|
2710
|
+
|
|
2711
|
+
Open the 'Document Model' creator at the bottom of connect to define your document mode with it's GraphQL Schema Definition.
|
|
2712
|
+
This schema will define the structure and fields for your document model using GraphQL.
|
|
2713
|
+
Follow one of our tutorials on Academy to get familiar with the process.
|
|
2714
|
+
|
|
2715
|
+
### 2. Defining Document Model Operations
|
|
2716
|
+
|
|
2717
|
+
Using the Document Model Operations Editor, define the operations for your document model and their GraphQL counterparts.
|
|
2718
|
+
These operations will handle state changes within your document model.
|
|
2719
|
+
|
|
2720
|
+
**Best Practices:**
|
|
2721
|
+
|
|
2722
|
+
- Clearly define CRUD operations (Create, Read, Update, Delete).
|
|
2723
|
+
- Use GraphQL input types to specify the parameters for each operation.
|
|
2724
|
+
- Ensure that operations align with user intent to maintain a clean and understandable API.
|
|
2725
|
+
|
|
2726
|
+
### 3. Generating Scaffolding Code
|
|
2727
|
+
|
|
2728
|
+
Export your document model as a .zip file from Connect.
|
|
2729
|
+
Import the .zip file into your project directory created in Step 1.
|
|
2730
|
+
Run the following command to generate the scaffolding code:
|
|
2731
|
+
|
|
2732
|
+
~~~bash
|
|
2733
|
+
npm run generate YourModelName.phdm.zip
|
|
2734
|
+
~~~
|
|
2735
|
+
|
|
2736
|
+
This will create a new directory under /document-models containing:
|
|
2737
|
+
|
|
2738
|
+
JSON file with the document model specification.
|
|
2739
|
+
GraphQL file with state and operation schemas.
|
|
2740
|
+
A gen/ folder with autogenerated code.
|
|
2741
|
+
A src/ folder for your custom code implementation.
|
|
2742
|
+
|
|
2743
|
+
### 4. Implementing Reducer Code and Unit Tests
|
|
2744
|
+
|
|
2745
|
+
Navigate to the reducer directory:
|
|
2746
|
+
|
|
2747
|
+
~~~bash
|
|
2748
|
+
cd document-models/"YourModelName"/src/reducers
|
|
2749
|
+
~~~
|
|
2750
|
+
|
|
2751
|
+
Implement the reducer functions for each document model operation. These functions will handle state transitions.
|
|
2752
|
+
|
|
2753
|
+
Add utility functions in:
|
|
2754
|
+
|
|
2755
|
+
~~~bash
|
|
2756
|
+
document-models/"YourModelName"/src/utils.ts
|
|
2757
|
+
~~~
|
|
2758
|
+
|
|
2759
|
+
Write unit tests to ensure the correctness of your reducers:
|
|
2760
|
+
|
|
2761
|
+
Test files should be located in:
|
|
2762
|
+
|
|
2763
|
+
~~~bash
|
|
2764
|
+
document-models/"YourModelName"/src/reducers/tests
|
|
2765
|
+
~~~
|
|
2766
|
+
|
|
2767
|
+
Run the tests:
|
|
2768
|
+
|
|
2769
|
+
~~~bash
|
|
2770
|
+
npm test
|
|
2771
|
+
~~~
|
|
2772
|
+
|
|
2773
|
+
Test the editor functionality:
|
|
2774
|
+
|
|
2775
|
+
~~~bash
|
|
2776
|
+
npm run connect
|
|
2777
|
+
~~~
|
|
2778
|
+
|
|
2779
|
+
### 5. Implementing Document Editors
|
|
2780
|
+
|
|
2781
|
+
Generate the editor template for your document model:
|
|
2782
|
+
|
|
2783
|
+
~~~bash
|
|
2784
|
+
npm run generate -- --editor YourModelName --document-types powerhouse/YourModelName
|
|
2785
|
+
~~~
|
|
2786
|
+
|
|
2787
|
+
The --editor flag specifies the name of your document model.
|
|
2788
|
+
The --document-types flag links the editor to your document model type.
|
|
2789
|
+
After generation:
|
|
2790
|
+
|
|
2791
|
+
Open the editor template:
|
|
2792
|
+
|
|
2793
|
+
~~~bash
|
|
2794
|
+
editors/YourModelName/editor.tsx
|
|
2795
|
+
~~~
|
|
2796
|
+
|
|
2797
|
+
Customize the editor interface to suit your document model.
|
|
2798
|
+
|
|
2799
|
+
### 6. Testing the Document Editor
|
|
2800
|
+
|
|
2801
|
+
Run the Connect app to test your document editor:
|
|
2802
|
+
|
|
2803
|
+
~~~bash
|
|
2804
|
+
npm run connect
|
|
2805
|
+
~~~
|
|
2806
|
+
|
|
2807
|
+
Verify that the editor functions as expected.
|
|
2808
|
+
Perform end-to-end testing to ensure smooth integration between the document model and its editor.
|
|
2809
|
+
|
|
2810
|
+
### 7. Adding a Manifest File
|
|
2811
|
+
|
|
2812
|
+
Create a manifest file to describe your document model and editor. This enables proper integration with the host application.
|
|
2813
|
+
|
|
2814
|
+
**Example manifest.json:**
|
|
2815
|
+
|
|
2816
|
+
~~~json
|
|
2817
|
+
{
|
|
2818
|
+
"name": "your-model-name",
|
|
2819
|
+
"description": "A brief description of your document model.",
|
|
2820
|
+
"category": "your-category", // e.g., "Finance", "People Ops", "Legal"
|
|
2821
|
+
"publisher": {
|
|
2822
|
+
"name": "your-publisher-name",
|
|
2823
|
+
"url": "your-publisher-url"
|
|
2824
|
+
},
|
|
2825
|
+
"documentModels": [
|
|
2826
|
+
{
|
|
2827
|
+
"id": "your-model-id",
|
|
2828
|
+
"name": "your-model-name"
|
|
2829
|
+
}
|
|
2830
|
+
],
|
|
2831
|
+
"editors": [
|
|
2832
|
+
{
|
|
2833
|
+
"id": "your-editor-id",
|
|
2834
|
+
"name": "your-editor-name",
|
|
2835
|
+
"documentTypes": ["your-model-id"]
|
|
2836
|
+
}
|
|
2837
|
+
]
|
|
2838
|
+
}
|
|
2839
|
+
~~~
|
|
2840
|
+
|
|
2841
|
+
### Steps to finalize:
|
|
2842
|
+
|
|
2843
|
+
Place the manifest file at your project root.
|
|
2844
|
+
Update your index.js to export your modules and include the new document model and editor.
|
|
2845
|
+
|
|
2846
|
+
### Final Thoughts
|
|
2847
|
+
|
|
2848
|
+
You've now successfully created a Document Model and its corresponding Editor using the Connect app!
|
|
2849
|
+
|
|
2850
|
+
Next Steps:
|
|
2851
|
+
|
|
2852
|
+
- Expand functionality: Add more operations or complex logic to your document model.
|
|
2853
|
+
- Improve UX: Enhance the document editor for a smoother user experience.
|
|
2854
|
+
- Integrate with other systems: Use APIs or GraphQL to connect your document model with external services.
|
|
2855
|
+
`.raw;
|
|
2856
|
+
//#endregion
|
|
2857
|
+
//#region src/templates/boilerplate/style.css.ts
|
|
2858
|
+
const styleTemplate = css`
|
|
2859
|
+
@import "tailwindcss";
|
|
2860
|
+
@import "@powerhousedao/design-system/theme.css";
|
|
2861
|
+
@import "@powerhousedao/connect/style.css";
|
|
2862
|
+
|
|
2863
|
+
@theme {
|
|
2864
|
+
/* You can customize the theme by overriding the theme variables here */
|
|
2865
|
+
/* See https://tailwindcss.com/docs/theme#using-a-custom-theme for details */
|
|
2866
|
+
|
|
2867
|
+
/* If you would prefer to use plain css, add your styles below this @theme tag as you normally would */
|
|
2868
|
+
|
|
2869
|
+
/* A sensible CSS reset is applied by default. If you would prefer to use a different reset or none at all, uncomment the following line */
|
|
2870
|
+
|
|
2871
|
+
/* --*: initial; */
|
|
2872
|
+
}
|
|
2873
|
+
`.raw;
|
|
2874
|
+
//#endregion
|
|
2875
|
+
//#region src/templates/boilerplate/subgraphs/index.ts
|
|
2876
|
+
const subgraphsIndexTemplate = "";
|
|
2877
|
+
//#endregion
|
|
2878
|
+
//#region src/templates/boilerplate/tsconfig.json.ts
|
|
2879
|
+
const tsconfigPathsTemplate = json`
|
|
2880
|
+
"document-models": [
|
|
2881
|
+
"./document-models/index.ts"
|
|
2882
|
+
],
|
|
2883
|
+
"document-models/*": [
|
|
2884
|
+
"./document-models/*/index.ts"
|
|
2885
|
+
],
|
|
2886
|
+
"editors": [
|
|
2887
|
+
"./editors/index.ts"
|
|
2888
|
+
],
|
|
2889
|
+
"editors/*": [
|
|
2890
|
+
"./editors/*/index.ts"
|
|
2891
|
+
],
|
|
2892
|
+
"processors/*": [
|
|
2893
|
+
"./processors/*/index.ts"
|
|
2894
|
+
],
|
|
2895
|
+
"subgraphs": [
|
|
2896
|
+
"./subgraphs/index.ts"
|
|
2897
|
+
],
|
|
2898
|
+
"subgraphs/*": [
|
|
2899
|
+
"./subgraphs/*/index.ts"
|
|
2900
|
+
]`.raw;
|
|
2901
|
+
const tsConfigTemplate = json`
|
|
2902
|
+
{
|
|
2903
|
+
"compilerOptions": {
|
|
2904
|
+
"outDir": "./dist",
|
|
2905
|
+
"rootDir": ".",
|
|
2906
|
+
// paths for easy access to project modules
|
|
2907
|
+
"paths": {
|
|
2908
|
+
${tsconfigPathsTemplate}
|
|
2909
|
+
},
|
|
2910
|
+
"module": "nodenext",
|
|
2911
|
+
"moduleDetection": "force",
|
|
2912
|
+
"target": "esnext",
|
|
2913
|
+
"jsx": "react-jsx",
|
|
2914
|
+
"types": ["node", "vitest/globals"],
|
|
2915
|
+
"lib": ["ESNext", "dom", "dom.iterable"],
|
|
2916
|
+
"declaration": true,
|
|
2917
|
+
"declarationMap": true,
|
|
2918
|
+
"emitDeclarationOnly": true,
|
|
2919
|
+
"strict": true,
|
|
2920
|
+
"verbatimModuleSyntax": true,
|
|
2921
|
+
"isolatedModules": true,
|
|
2922
|
+
"noUncheckedSideEffectImports": true,
|
|
2923
|
+
"skipLibCheck": true
|
|
2924
|
+
},
|
|
2925
|
+
"include": ["**/*", "./powerhouse.manifest.json"],
|
|
2926
|
+
"exclude": ["dist", "node_modules", ".ph"]
|
|
2927
|
+
}
|
|
2928
|
+
`.raw;
|
|
2929
|
+
//#endregion
|
|
2930
|
+
//#region src/templates/boilerplate/vite.config.ts.ts
|
|
2931
|
+
const viteConfigTemplate = ts`
|
|
2932
|
+
import { getConnectBaseViteConfig } from "@powerhousedao/builder-tools";
|
|
2933
|
+
import { defineConfig, mergeConfig, type UserConfig } from "vite";
|
|
2934
|
+
|
|
2935
|
+
export default defineConfig(({ mode }) => {
|
|
2936
|
+
const baseConnectViteConfig = getConnectBaseViteConfig({
|
|
2937
|
+
mode,
|
|
2938
|
+
dirname: import.meta.dirname,
|
|
2939
|
+
});
|
|
2940
|
+
|
|
2941
|
+
const additionalViteConfig: UserConfig = {
|
|
2942
|
+
// add your own vite config here
|
|
2943
|
+
};
|
|
2944
|
+
|
|
2945
|
+
const config = mergeConfig(baseConnectViteConfig, additionalViteConfig);
|
|
2946
|
+
|
|
2947
|
+
return config;
|
|
2948
|
+
});
|
|
2949
|
+
`;
|
|
2950
|
+
//#endregion
|
|
2951
|
+
//#region src/templates/boilerplate/vitest.config.ts.ts
|
|
2952
|
+
const vitestConfigTemplate = ts`
|
|
2953
|
+
import { defineConfig } from "vitest/config";
|
|
2954
|
+
import react from "@vitejs/plugin-react";
|
|
2955
|
+
import tsconfigPaths from "vite-tsconfig-paths";
|
|
2956
|
+
|
|
2957
|
+
export default defineConfig({
|
|
2958
|
+
test: {
|
|
2959
|
+
globals: true,
|
|
2960
|
+
},
|
|
2961
|
+
plugins: [tsconfigPaths(), react()],
|
|
2962
|
+
});
|
|
2963
|
+
|
|
2964
|
+
`;
|
|
2965
|
+
//#endregion
|
|
2966
|
+
//#region src/templates/cli-docs/docs-from-cli-help.ts
|
|
2967
|
+
function groupHelpTopicsByCategory(helpTopics) {
|
|
2968
|
+
const helpTopicsByCategory = {};
|
|
2969
|
+
for (const helpTopic of helpTopics) if (!helpTopicsByCategory[helpTopic.category]) helpTopicsByCategory[helpTopic.category] = [helpTopic];
|
|
2970
|
+
else helpTopicsByCategory[helpTopic.category]?.push(helpTopic);
|
|
2971
|
+
return helpTopicsByCategory;
|
|
2972
|
+
}
|
|
2973
|
+
function makeTableOfContents(commandsHelpInfo) {
|
|
2974
|
+
const commandNames = commandsHelpInfo.map(({ name }) => name);
|
|
2975
|
+
const tableOfContentsEntries = [];
|
|
2976
|
+
for (const name of commandNames) tableOfContentsEntries.push(`- [${capitalCase(name)}](#${kebabCase(name)})\n`);
|
|
2977
|
+
return tableOfContentsEntries.join("");
|
|
2978
|
+
}
|
|
2979
|
+
function makeDefaultsDescriptors(defaults) {
|
|
2980
|
+
return defaults.filter((d) => d !== "optional" && d !== "[...optional]").map((d) => {
|
|
2981
|
+
const [label, ...rest] = d.split(":").map((s) => s.trim());
|
|
2982
|
+
return `**${label}**: \`${rest.join("")}\``;
|
|
2983
|
+
});
|
|
2984
|
+
}
|
|
2985
|
+
function makeRequiredDescriptor(defaults) {
|
|
2986
|
+
if (defaults.includes("optional") || defaults.includes("[...optional]") || defaults.some((d) => d.includes("default"))) return "";
|
|
2987
|
+
return "*[required]*";
|
|
2988
|
+
}
|
|
2989
|
+
function makeHeadingFromUsage(usage) {
|
|
2990
|
+
if (usage.includes("--")) return `#### ${capitalCase(usage.split(" ")[0].replace("--", ""))}`;
|
|
2991
|
+
return `#### ${capitalCase(usage.replace("[", "").replace("]", ""))}`;
|
|
2992
|
+
}
|
|
2993
|
+
function makeCommandHelpTopicDocs(helpTopic) {
|
|
2994
|
+
const { defaults, description, usage } = helpTopic;
|
|
2995
|
+
return `${makeHeadingFromUsage(usage)} ${makeRequiredDescriptor(defaults)}<br>
|
|
2996
|
+
${description}<br><br>
|
|
2997
|
+
**usage:** \`${usage}\`<br>
|
|
2998
|
+
${makeDefaultsDescriptors(defaults).join("<br>")}
|
|
2999
|
+
`;
|
|
3000
|
+
}
|
|
3001
|
+
function makeCommandHelpTopicsDocs(helpTopics) {
|
|
3002
|
+
return helpTopics.map(makeCommandHelpTopicDocs).join("");
|
|
3003
|
+
}
|
|
3004
|
+
function makeCommandHelpTopicsDocsForCategories(helpTopics) {
|
|
3005
|
+
const helpTopicsByCategory = groupHelpTopicsByCategory(helpTopics);
|
|
3006
|
+
const helpTopicsDocs = [];
|
|
3007
|
+
for (const [category, helpTopics] of Object.entries(helpTopicsByCategory)) {
|
|
3008
|
+
const helpTopicDocs = makeCommandHelpTopicsDocs(helpTopics ?? []);
|
|
3009
|
+
helpTopicsDocs.push(`### ${category}
|
|
3010
|
+
${helpTopicDocs}
|
|
3011
|
+
`);
|
|
3012
|
+
}
|
|
3013
|
+
return helpTopicsDocs.join("");
|
|
3014
|
+
}
|
|
3015
|
+
function makeCommandDoc(commandHelpInfo) {
|
|
3016
|
+
const { name, description, helpTopics } = commandHelpInfo;
|
|
3017
|
+
return `## ${capitalCase(name)}
|
|
3018
|
+
${description}
|
|
3019
|
+
${makeCommandHelpTopicsDocsForCategories(helpTopics)}`;
|
|
3020
|
+
}
|
|
3021
|
+
function makeCommandDocs(commandsHelpInfo) {
|
|
3022
|
+
return commandsHelpInfo.map(makeCommandDoc).join("");
|
|
3023
|
+
}
|
|
3024
|
+
const docsFromCliHelpTemplate = (v) => `# ${v.docsTitle}<br>
|
|
3025
|
+
${v.docsIntroduction}<br><br>
|
|
3026
|
+
${v.cliDescription}<br>
|
|
3027
|
+
## Table of Contents
|
|
3028
|
+
${makeTableOfContents(v.commandsHelpInfo)}<br>
|
|
3029
|
+
${makeCommandDocs(v.commandsHelpInfo)}
|
|
3030
|
+
`;
|
|
3031
|
+
//#endregion
|
|
3032
|
+
//#region src/templates/document-editor/editor.ts
|
|
3033
|
+
const documentEditorEditorFileTemplate = (v) => tsx`
|
|
3034
|
+
import { DocumentStateViewer, DocumentToolbar } from "@powerhousedao/design-system/connect";
|
|
3035
|
+
import { ${v.useSelectedDocumentHookName}, actions } from "${v.documentModelImportPath}";
|
|
3036
|
+
|
|
3037
|
+
export default function Editor() {
|
|
3038
|
+
const [document, dispatch] = ${v.useSelectedDocumentHookName}();
|
|
3039
|
+
|
|
3040
|
+
const handleSetName = (name: string) => {
|
|
3041
|
+
// 'actions' contains all available actions for this document type
|
|
3042
|
+
dispatch(actions.setName(name));
|
|
3043
|
+
};
|
|
3044
|
+
|
|
3045
|
+
return (
|
|
3046
|
+
<div className="mx-auto max-w-4xl bg-gray-50 p-6">
|
|
3047
|
+
<DocumentToolbar />
|
|
3048
|
+
|
|
3049
|
+
{/* "ph-default-styles" sets default styles for basic UI elements */}
|
|
3050
|
+
<div className="ph-default-styles">
|
|
3051
|
+
{/* Edit document name */}
|
|
3052
|
+
<label className="my-6">
|
|
3053
|
+
<h3>Document Name</h3>
|
|
3054
|
+
<input
|
|
3055
|
+
type="text"
|
|
3056
|
+
defaultValue={document.header.name}
|
|
3057
|
+
placeholder="Enter document name..."
|
|
3058
|
+
title="Edit document name and click outside to save."
|
|
3059
|
+
autoFocus
|
|
3060
|
+
onBlur={(e) => handleSetName(e.target.value.trim())}
|
|
3061
|
+
onKeyDown={(e) => {
|
|
3062
|
+
if (e.key === "Enter") {
|
|
3063
|
+
e.currentTarget.blur();
|
|
3064
|
+
}
|
|
3065
|
+
}}
|
|
3066
|
+
className="font-semibold"
|
|
3067
|
+
/>
|
|
3068
|
+
</label>
|
|
3069
|
+
<hr />
|
|
3070
|
+
|
|
3071
|
+
{/* Document header info */}
|
|
3072
|
+
<div className="mb-6 grid grid-cols-2 gap-x-8">
|
|
3073
|
+
<label>
|
|
3074
|
+
<h3 className="text-base">ID</h3>
|
|
3075
|
+
<input
|
|
3076
|
+
type="text"
|
|
3077
|
+
value={document.header.id}
|
|
3078
|
+
readOnly
|
|
3079
|
+
className="font-mono"
|
|
3080
|
+
/>
|
|
3081
|
+
</label>
|
|
3082
|
+
<label>
|
|
3083
|
+
<h3 className="text-base">Created</h3>
|
|
3084
|
+
<input
|
|
3085
|
+
type="text"
|
|
3086
|
+
value={new Date(document.header.createdAtUtcIso).toLocaleString()}
|
|
3087
|
+
readOnly
|
|
3088
|
+
/>
|
|
3089
|
+
</label>
|
|
3090
|
+
<label>
|
|
3091
|
+
<h3 className="text-base">Type</h3>
|
|
3092
|
+
<input type="text" value={document.header.documentType} readOnly />
|
|
3093
|
+
</label>
|
|
3094
|
+
<label>
|
|
3095
|
+
<h3 className="text-base">Last Modified</h3>
|
|
3096
|
+
<input
|
|
3097
|
+
type="text"
|
|
3098
|
+
value={new Date(
|
|
3099
|
+
document.header.lastModifiedAtUtcIso,
|
|
3100
|
+
).toLocaleString()}
|
|
3101
|
+
readOnly
|
|
3102
|
+
/>
|
|
3103
|
+
</label>
|
|
3104
|
+
</div>
|
|
3105
|
+
|
|
3106
|
+
{/* Document state */}
|
|
3107
|
+
<div className="mt-6">
|
|
3108
|
+
<h3 className="text-base">Document State</h3>
|
|
3109
|
+
<DocumentStateViewer state={document.state} />
|
|
3110
|
+
</div>
|
|
3111
|
+
</div>
|
|
3112
|
+
</div>
|
|
3113
|
+
);
|
|
3114
|
+
}
|
|
3115
|
+
`.raw;
|
|
3116
|
+
//#endregion
|
|
3117
|
+
//#region src/templates/document-editor/module.ts
|
|
3118
|
+
const documentEditorModuleFileTemplate = (v) => tsx`
|
|
3119
|
+
import type { EditorModule } from "document-model";
|
|
3120
|
+
import { lazy } from "react";
|
|
3121
|
+
|
|
3122
|
+
/** Document editor module for the "${v.documentTypes}" document type */
|
|
3123
|
+
export const ${v.pascalCaseEditorName}: EditorModule = {
|
|
3124
|
+
Component: lazy(() => import("./editor.js")),
|
|
3125
|
+
documentTypes: ${v.documentTypes},
|
|
3126
|
+
config: {
|
|
3127
|
+
id: "${v.editorId}",
|
|
3128
|
+
name: "${v.editorName}",
|
|
3129
|
+
},
|
|
3130
|
+
};
|
|
3131
|
+
`.raw;
|
|
3132
|
+
//#endregion
|
|
3133
|
+
//#region src/templates/document-model/actions.ts
|
|
3134
|
+
function buildModuleActionsName(module, camelCaseDocumentType) {
|
|
3135
|
+
const camelCaseModuleName = camelCase(module.name);
|
|
3136
|
+
return `${camelCaseDocumentType}${camelCaseModuleName.charAt(0).toUpperCase()}${camelCaseModuleName.slice(1)}Actions`;
|
|
3137
|
+
}
|
|
3138
|
+
function buildModuleActionsNames(modules, camelCaseDocumentType) {
|
|
3139
|
+
return modules.map((m) => buildModuleActionsName(m, camelCaseDocumentType));
|
|
3140
|
+
}
|
|
3141
|
+
function buildModuleActionsImports(modules, camelCaseDocumentType) {
|
|
3142
|
+
return `import { ${buildModuleActionsNames(modules, camelCaseDocumentType).join(",\n")} } from "./gen/creators.js";`;
|
|
3143
|
+
}
|
|
3144
|
+
function buildModuleActionsSpreadExport(modules, camelCaseDocumentType) {
|
|
3145
|
+
return `
|
|
3146
|
+
export const actions = { ...baseActions, ${buildModuleActionsNames(modules, camelCaseDocumentType).map((n) => `...${n}`).join(",\n")} }`;
|
|
3147
|
+
}
|
|
3148
|
+
const documentModelRootActionsFileTemplate = (v) => ts`
|
|
3149
|
+
import { baseActions } from "document-model";
|
|
3150
|
+
${buildModuleActionsImports(v.modules, v.camelCaseDocumentType)}
|
|
3151
|
+
|
|
3152
|
+
/** Actions for the ${v.pascalCaseDocumentType} document model */
|
|
3153
|
+
${buildModuleActionsSpreadExport(v.modules, v.camelCaseDocumentType)}
|
|
3154
|
+
`.raw;
|
|
3155
|
+
//#endregion
|
|
3156
|
+
//#region src/templates/document-model/gen/actions.ts
|
|
3157
|
+
function makeModuleActionsTypeImport(module, pascalCaseDocumentType) {
|
|
3158
|
+
return `import type { ${pascalCaseDocumentType}${pascalCase(module.name)}Action } from "./${kebabCase(module.name)}/actions.js";`;
|
|
3159
|
+
}
|
|
3160
|
+
function makeModuleActionsTypeImports(modules, pascalCaseDocumentType) {
|
|
3161
|
+
return modules.map((module) => makeModuleActionsTypeImport(module, pascalCaseDocumentType)).join("\n");
|
|
3162
|
+
}
|
|
3163
|
+
function makeModuleActionsTypeExport(module) {
|
|
3164
|
+
return `export * from "./${kebabCase(module.name)}/actions.js";`;
|
|
3165
|
+
}
|
|
3166
|
+
function makeModuleActionsTypeExports(modules) {
|
|
3167
|
+
return modules.map(makeModuleActionsTypeExport).join("\n");
|
|
3168
|
+
}
|
|
3169
|
+
function makeModuleActionTypeName(module, pascalCaseDocumentType) {
|
|
3170
|
+
return `${pascalCaseDocumentType}${pascalCase(module.name)}Action`;
|
|
3171
|
+
}
|
|
3172
|
+
function makeModuleActionTypesUnion(modules, pascalCaseDocumentType) {
|
|
3173
|
+
return modules.map((module) => makeModuleActionTypeName(module, pascalCaseDocumentType)).join("|\n");
|
|
3174
|
+
}
|
|
3175
|
+
function makeDocumentActionType(modules, pascalCaseDocumentType) {
|
|
3176
|
+
return `export type ${pascalCaseDocumentType}Action = ${makeModuleActionTypesUnion(modules, pascalCaseDocumentType)};`;
|
|
3177
|
+
}
|
|
3178
|
+
const documentModelGenActionsFileTemplate = (v) => ts`
|
|
3179
|
+
${makeModuleActionsTypeImports(v.modules, v.pascalCaseDocumentType)}
|
|
3180
|
+
|
|
3181
|
+
${makeModuleActionsTypeExports(v.modules)}
|
|
3182
|
+
|
|
3183
|
+
${makeDocumentActionType(v.modules, v.pascalCaseDocumentType)}
|
|
3184
|
+
`.raw;
|
|
3185
|
+
//#endregion
|
|
3186
|
+
//#region src/templates/document-model/gen/controller.ts
|
|
3187
|
+
const documentModelGenControllerFileTemplate = (v) => ts`
|
|
3188
|
+
import { PHDocumentController } from "document-model";
|
|
3189
|
+
import { ${v.pascalCaseDocumentType} } from "../module.js";
|
|
3190
|
+
import type { ${v.actionTypeName}, ${v.phStateName} } from "./types.js";
|
|
3191
|
+
|
|
3192
|
+
export const ${v.pascalCaseDocumentType}Controller = PHDocumentController.forDocumentModel<
|
|
3193
|
+
${v.phStateName},
|
|
3194
|
+
${v.actionTypeName}
|
|
3195
|
+
>(${v.pascalCaseDocumentType});
|
|
3196
|
+
`.raw;
|
|
3197
|
+
//#endregion
|
|
3198
|
+
//#region src/templates/document-model/gen/creators.ts
|
|
3199
|
+
function buildModuleCreatorsExport(module, camelCaseDocumentType) {
|
|
3200
|
+
const kebabCaseModuleName = kebabCase(module.name);
|
|
3201
|
+
const camelCaseModuleName = camelCase(module.name);
|
|
3202
|
+
const namespaceName = `${camelCaseDocumentType}${camelCaseModuleName.charAt(0).toUpperCase()}${camelCaseModuleName.slice(1)}Actions`;
|
|
3203
|
+
return [`export * from "./${kebabCaseModuleName}/creators.js";`, `export * as ${namespaceName} from "./${kebabCaseModuleName}/creators.js";`];
|
|
3204
|
+
}
|
|
3205
|
+
function buildCreatorsExports(modules, camelCaseDocumentType) {
|
|
3206
|
+
return modules.flatMap((module) => buildModuleCreatorsExport(module, camelCaseDocumentType)).join("\n");
|
|
3207
|
+
}
|
|
3208
|
+
const documentModelGenCreatorsFileTemplate = (v) => ts`
|
|
3209
|
+
${buildCreatorsExports(v.modules, v.camelCaseDocumentType)}
|
|
3210
|
+
`.raw;
|
|
3211
|
+
//#endregion
|
|
3212
|
+
//#region src/templates/document-model/gen/document-schema.ts
|
|
3213
|
+
const documentModelDocumentSchemaFileTemplate = (v) => ts`
|
|
3214
|
+
import {
|
|
3215
|
+
BaseDocumentHeaderSchema,
|
|
3216
|
+
BaseDocumentStateSchema,
|
|
3217
|
+
} from "document-model";
|
|
3218
|
+
import { z } from "zod";
|
|
3219
|
+
import { ${v.documentTypeVariableName} } from "./document-type.js";
|
|
3220
|
+
import { ${v.stateSchemaName} } from "./schema/zod.js";
|
|
3221
|
+
import type { ${v.phDocumentTypeName}, ${v.phStateName} } from "./types.js";
|
|
3222
|
+
|
|
3223
|
+
/** Schema for validating the header object of a ${v.pascalCaseDocumentType} document */
|
|
3224
|
+
export const ${v.phDocumentTypeName}HeaderSchema = BaseDocumentHeaderSchema.extend({
|
|
3225
|
+
documentType: z.literal(${v.documentTypeVariableName}),
|
|
3226
|
+
});
|
|
3227
|
+
|
|
3228
|
+
/** Schema for validating the state object of a ${v.pascalCaseDocumentType} document */
|
|
3229
|
+
export const ${v.phStateName}Schema = BaseDocumentStateSchema.extend({
|
|
3230
|
+
global: ${v.stateSchemaName}(),
|
|
3231
|
+
});
|
|
3232
|
+
|
|
3233
|
+
export const ${v.phDocumentSchemaName} = z.object({
|
|
3234
|
+
header: ${v.phDocumentTypeName}HeaderSchema,
|
|
3235
|
+
state: ${v.phStateName}Schema,
|
|
3236
|
+
initialState: ${v.phStateName}Schema,
|
|
3237
|
+
});
|
|
3238
|
+
|
|
3239
|
+
/** Simple helper function to check if a state object is a ${v.pascalCaseDocumentType} document state object */
|
|
3240
|
+
export function ${v.isPhStateOfTypeFunctionName}(
|
|
3241
|
+
state: unknown,
|
|
3242
|
+
): state is ${v.phStateName} {
|
|
3243
|
+
return ${v.phStateName}Schema.safeParse(state).success;
|
|
3244
|
+
}
|
|
3245
|
+
|
|
3246
|
+
/** Simple helper function to assert that a document state object is a ${v.pascalCaseDocumentType} document state object */
|
|
3247
|
+
export function ${v.assertIsPhStateOfTypeFunctionName}(
|
|
3248
|
+
state: unknown,
|
|
3249
|
+
): asserts state is ${v.phStateName} {
|
|
3250
|
+
${v.phStateName}Schema.parse(state);
|
|
3251
|
+
}
|
|
3252
|
+
|
|
3253
|
+
/** Simple helper function to check if a document is a ${v.pascalCaseDocumentType} document */
|
|
3254
|
+
export function ${v.isPhDocumentOfTypeFunctionName}(
|
|
3255
|
+
document: unknown,
|
|
3256
|
+
): document is ${v.phDocumentTypeName} {
|
|
3257
|
+
return ${v.phDocumentSchemaName}.safeParse(document).success;
|
|
3258
|
+
}
|
|
3259
|
+
|
|
3260
|
+
/** Simple helper function to assert that a document is a ${v.pascalCaseDocumentType} document */
|
|
3261
|
+
export function ${v.assertIsPhDocumentOfTypeFunctionName}(
|
|
3262
|
+
document: unknown,
|
|
3263
|
+
): asserts document is ${v.phDocumentTypeName} {
|
|
3264
|
+
${v.phDocumentSchemaName}.parse(document);
|
|
3265
|
+
}
|
|
3266
|
+
`.raw;
|
|
3267
|
+
//#endregion
|
|
3268
|
+
//#region src/templates/document-model/gen/document-type.ts
|
|
3269
|
+
const documentModelDocumentTypeTemplate = (v) => ts`
|
|
3270
|
+
export const ${v.documentTypeVariableName} = "${v.documentTypeId}";
|
|
3271
|
+
`.raw;
|
|
3272
|
+
//#endregion
|
|
3273
|
+
//#region src/templates/document-model/gen/index.ts
|
|
3274
|
+
function buildModuleOperationsExports(module) {
|
|
3275
|
+
return `export * from "./${kebabCase(module.name)}/operations.js";`;
|
|
3276
|
+
}
|
|
3277
|
+
function buildModulesOperationsExports(modules) {
|
|
3278
|
+
return modules.map(buildModuleOperationsExports).join("\n");
|
|
3279
|
+
}
|
|
3280
|
+
const documentModelGenIndexFileTemplate = (v) => ts`
|
|
3281
|
+
export * from './actions.js';
|
|
3282
|
+
export * from './document-model.js';
|
|
3283
|
+
export * from './types.js';
|
|
3284
|
+
export * from './creators.js';
|
|
3285
|
+
export {
|
|
3286
|
+
create${v.phDocumentTypeName},
|
|
3287
|
+
createState,
|
|
3288
|
+
defaultPHState,
|
|
3289
|
+
defaultGlobalState,
|
|
3290
|
+
defaultLocalState,
|
|
3291
|
+
} from './ph-factories.js';
|
|
3292
|
+
export * from "./utils.js";
|
|
3293
|
+
export * from "./reducer.js";
|
|
3294
|
+
export * from "./controller.js";
|
|
3295
|
+
export * from "./schema/index.js";
|
|
3296
|
+
export * from "./document-type.js";
|
|
3297
|
+
export * from "./document-schema.js";
|
|
3298
|
+
${buildModulesOperationsExports(v.modules)}
|
|
3299
|
+
`.raw;
|
|
3300
|
+
//#endregion
|
|
3301
|
+
//#region src/templates/document-model/gen/modules/actions.ts
|
|
3302
|
+
function getActionTypeExport(action) {
|
|
3303
|
+
const baseActionTypeName = action.hasAttachment ? "ActionWithAttachment" : "Action";
|
|
3304
|
+
const actionTypeName = getActionTypeName(action);
|
|
3305
|
+
const actionInputName = getActionInputName(action) ?? `"{}"`;
|
|
3306
|
+
return ts`export type ${actionTypeName} = ${baseActionTypeName} & { type: "${getActionType(action)}"; input: ${actionInputName} };`.raw;
|
|
3307
|
+
}
|
|
3308
|
+
function getActionTypeExports(actions) {
|
|
3309
|
+
return actions.map(getActionTypeExport).join("\n");
|
|
3310
|
+
}
|
|
3311
|
+
function getModuleExportType(actions, pascalCaseDocumentName, pascalCaseModuleName) {
|
|
3312
|
+
return ts`export type ${pascalCaseDocumentName}${pascalCaseModuleName}Action = ${actions.map(getActionTypeName).join(" |\n")};`.raw;
|
|
3313
|
+
}
|
|
3314
|
+
function getDocumentModelActionTypeImportNames(actions) {
|
|
3315
|
+
const actionTypeImports = ["Action"];
|
|
3316
|
+
if (actions.some((a) => a.hasAttachment)) actionTypeImports.push("ActionWithAttachment");
|
|
3317
|
+
return actionTypeImports.join(",\n");
|
|
3318
|
+
}
|
|
3319
|
+
const documentModelOperationModuleActionsFileTemplate = (v) => ts`
|
|
3320
|
+
import type { ${getDocumentModelActionTypeImportNames(v.actions)} } from 'document-model';
|
|
3321
|
+
import type {
|
|
3322
|
+
${getActionInputTypeNames(v.actions)}
|
|
3323
|
+
} from '../types.js';
|
|
3324
|
+
|
|
3325
|
+
${getActionTypeExports(v.actions)}
|
|
3326
|
+
|
|
3327
|
+
${getModuleExportType(v.actions, v.pascalCaseDocumentType, v.pascalCaseModuleName)}
|
|
3328
|
+
`.raw;
|
|
3329
|
+
//#endregion
|
|
3330
|
+
//#region src/templates/document-model/gen/modules/creators.ts
|
|
3331
|
+
function makeDocumentModelTypeImports(actions) {
|
|
3332
|
+
const actionTypeImports = ["createAction"];
|
|
3333
|
+
if (actions.some((a) => a.hasAttachment)) actionTypeImports.push("AttachmentInput");
|
|
3334
|
+
return actionTypeImports.join(",\n");
|
|
3335
|
+
}
|
|
3336
|
+
function makeActionInputSchemaName(action) {
|
|
3337
|
+
if (!action.hasInput) return;
|
|
3338
|
+
return `${pascalCase(action.name)}InputSchema`;
|
|
3339
|
+
}
|
|
3340
|
+
function makeActionInputTypeName(action) {
|
|
3341
|
+
if (!action.hasInput) return;
|
|
3342
|
+
return `${pascalCase(action.name)}Input`;
|
|
3343
|
+
}
|
|
3344
|
+
function makeActionTypeName(action) {
|
|
3345
|
+
return `${pascalCase(action.name)}Action`;
|
|
3346
|
+
}
|
|
3347
|
+
function makeActionInputSchemaImports(actions) {
|
|
3348
|
+
return actions.map(makeActionInputSchemaName).filter(Boolean).join(",\n");
|
|
3349
|
+
}
|
|
3350
|
+
function makeActionInputTypeImports(actions) {
|
|
3351
|
+
return actions.map(makeActionInputTypeName).filter(Boolean).join(",\n");
|
|
3352
|
+
}
|
|
3353
|
+
function makeActionTypeImports(actions) {
|
|
3354
|
+
return actions.map(makeActionTypeName).join(",\n");
|
|
3355
|
+
}
|
|
3356
|
+
function makeActionCreatorWithInput(actionWithInput) {
|
|
3357
|
+
if (!actionWithInput.hasInput) return;
|
|
3358
|
+
const camelCaseActionName = camelCase(actionWithInput.name);
|
|
3359
|
+
const constantCaseActionName = constantCase(actionWithInput.name);
|
|
3360
|
+
const actionTypeName = makeActionTypeName(actionWithInput);
|
|
3361
|
+
const inputSchemaName = makeActionInputSchemaName(actionWithInput);
|
|
3362
|
+
const inputTypeName = makeActionInputTypeName(actionWithInput);
|
|
3363
|
+
const argsArray = [actionWithInput.isEmptyInput ? `input: ${inputTypeName} = {}` : `input: ${inputTypeName}`];
|
|
3364
|
+
if (actionWithInput.hasAttachment) argsArray.push(`attachments: AttachmentInput[]`);
|
|
3365
|
+
return ts`
|
|
3366
|
+
export const ${camelCaseActionName} = (${argsArray.join(", ")}) =>
|
|
3367
|
+
createAction<${actionTypeName}>(
|
|
3368
|
+
"${constantCaseActionName}",
|
|
3369
|
+
{...input},
|
|
3370
|
+
${actionWithInput.hasAttachment ? "attachments" : "undefined"},
|
|
3371
|
+
${inputSchemaName},
|
|
3372
|
+
"${actionWithInput.scope}"
|
|
3373
|
+
);`.raw;
|
|
3374
|
+
}
|
|
3375
|
+
function makeActionCreatorWithoutInput(actionWithoutInput) {
|
|
3376
|
+
if (actionWithoutInput.hasInput) return;
|
|
3377
|
+
const camelCaseActionName = camelCase(actionWithoutInput.name);
|
|
3378
|
+
const constantCaseActionName = constantCase(actionWithoutInput.name);
|
|
3379
|
+
return ts`
|
|
3380
|
+
export const ${camelCaseActionName} = () =>
|
|
3381
|
+
createAction<${makeActionTypeName(actionWithoutInput)}>("${constantCaseActionName}");`.raw;
|
|
3382
|
+
}
|
|
3383
|
+
function makeCreatorsForActionsWithInput(actions) {
|
|
3384
|
+
return actions.filter((a) => a.hasInput).map(makeActionCreatorWithInput).join("\n\n");
|
|
3385
|
+
}
|
|
3386
|
+
function makeActionCreatorsWithoutInput(actions) {
|
|
3387
|
+
return actions.filter((a) => !a.hasInput).map(makeActionCreatorWithoutInput).join("\n\n");
|
|
3388
|
+
}
|
|
3389
|
+
const documentModelOperationsModuleCreatorsFileTemplate = (v) => ts`
|
|
3390
|
+
|
|
3391
|
+
import { ${makeDocumentModelTypeImports(v.actions)} } from "document-model";
|
|
3392
|
+
import {
|
|
3393
|
+
${makeActionInputSchemaImports(v.actions)}
|
|
3394
|
+
} from '../schema/zod.js';
|
|
3395
|
+
import type {
|
|
3396
|
+
${makeActionInputTypeImports(v.actions)}
|
|
3397
|
+
} from '../types.js';
|
|
3398
|
+
import type {
|
|
3399
|
+
${makeActionTypeImports(v.actions)}
|
|
3400
|
+
} from './actions.js';
|
|
3401
|
+
|
|
3402
|
+
${makeCreatorsForActionsWithInput(v.actions)}
|
|
3403
|
+
|
|
3404
|
+
${makeActionCreatorsWithoutInput(v.actions)}
|
|
3405
|
+
`.raw;
|
|
3406
|
+
//#endregion
|
|
3407
|
+
//#region src/templates/document-model/gen/modules/error.ts
|
|
3408
|
+
function getErrorName(error) {
|
|
3409
|
+
if (!error.name) return;
|
|
3410
|
+
return pascalCase(error.name);
|
|
3411
|
+
}
|
|
3412
|
+
function getErrorNames(errors) {
|
|
3413
|
+
return errors.map(getErrorName).filter((name) => name !== void 0);
|
|
3414
|
+
}
|
|
3415
|
+
function getErrorCodeType(errors) {
|
|
3416
|
+
return ts`export type ErrorCode = ${getErrorNames(errors).map((name) => `"${name}"`).join(" |\n")};`.raw;
|
|
3417
|
+
}
|
|
3418
|
+
function errorClassTemplate(error) {
|
|
3419
|
+
const errorName = getErrorName(error);
|
|
3420
|
+
if (!errorName) return;
|
|
3421
|
+
return ts`
|
|
3422
|
+
export class ${errorName} extends Error implements ReducerError {
|
|
3423
|
+
errorCode = "${errorName}" as ErrorCode;
|
|
3424
|
+
constructor(message = "${errorName}") {
|
|
3425
|
+
super(message);
|
|
3426
|
+
}
|
|
3427
|
+
}
|
|
3428
|
+
`.raw;
|
|
3429
|
+
}
|
|
3430
|
+
function getErrorClassImplementations(errors) {
|
|
3431
|
+
return errors.map((error) => errorClassTemplate(error)).filter(Boolean).join("\n\n");
|
|
3432
|
+
}
|
|
3433
|
+
function getErrorsImplementations(errors) {
|
|
3434
|
+
if (!errors.length) return "";
|
|
3435
|
+
const deduplicatedErrors = errors.reduce((acc, error) => {
|
|
3436
|
+
if (!acc.some((e) => getErrorName(e) === getErrorName(error))) acc.push(error);
|
|
3437
|
+
return acc;
|
|
3438
|
+
}, new Array());
|
|
3439
|
+
return ts`
|
|
3440
|
+
${getErrorCodeType(deduplicatedErrors)}
|
|
3441
|
+
|
|
3442
|
+
export interface ReducerError {
|
|
3443
|
+
errorCode: ErrorCode;
|
|
3444
|
+
}
|
|
3445
|
+
|
|
3446
|
+
${getErrorClassImplementations(deduplicatedErrors)}
|
|
3447
|
+
`.raw;
|
|
3448
|
+
}
|
|
3449
|
+
function getActionErrorsExport(action) {
|
|
3450
|
+
const errors = action.errors;
|
|
3451
|
+
if (errors.length === 0) return;
|
|
3452
|
+
return ts`
|
|
3453
|
+
${pascalCase(action.name)}: { ${getErrorNames(errors).filter(Boolean).join(",\n")} }
|
|
3454
|
+
`.raw;
|
|
3455
|
+
}
|
|
3456
|
+
function getErrorsExport(actions) {
|
|
3457
|
+
return ts`
|
|
3458
|
+
export const errors = { ${actions.map(getActionErrorsExport).filter(Boolean).join(",\n")} };
|
|
3459
|
+
`.raw;
|
|
3460
|
+
}
|
|
3461
|
+
const documentModelOperationsModuleErrorFileTemplate = (v) => ts`
|
|
3462
|
+
${getErrorsImplementations(v.errors)}
|
|
3463
|
+
${getErrorsExport(v.actions)}
|
|
3464
|
+
`.raw;
|
|
3465
|
+
//#endregion
|
|
3466
|
+
//#region src/templates/document-model/gen/modules/operations.ts
|
|
3467
|
+
function getActionTypeNames(actions) {
|
|
3468
|
+
return actions.map(getActionTypeName);
|
|
3469
|
+
}
|
|
3470
|
+
function getActionTypeImports(actions) {
|
|
3471
|
+
return getActionTypeNames(actions).join(",\n");
|
|
3472
|
+
}
|
|
3473
|
+
function getOperationsInterfaceName(pascalCaseDocumentType, module) {
|
|
3474
|
+
return `${pascalCaseDocumentType}${pascalCase(module.name)}Operations`;
|
|
3475
|
+
}
|
|
3476
|
+
function getActionOperationFieldName(action) {
|
|
3477
|
+
return `${camelCase(action.name)}Operation`;
|
|
3478
|
+
}
|
|
3479
|
+
function getActionOperationStateTypeName(action, pascalCaseDocumentType) {
|
|
3480
|
+
if (!action.state) return `${pascalCaseDocumentType}State`;
|
|
3481
|
+
return `${pascalCaseDocumentType}_${pascalCase(action.state)}_State`;
|
|
3482
|
+
}
|
|
3483
|
+
function getActionOperationStateTypeImports(actions, pascalCaseDocumentType) {
|
|
3484
|
+
const stateTypeNames = actions.map((action) => getActionOperationStateTypeName(action, pascalCaseDocumentType));
|
|
3485
|
+
return Array.from(new Set(stateTypeNames)).join(",\n");
|
|
3486
|
+
}
|
|
3487
|
+
function getActionOperationFunction(action, pascalCaseDocumentType) {
|
|
3488
|
+
return ts`
|
|
3489
|
+
(state: ${getActionOperationStateTypeName(action, pascalCaseDocumentType)}, action: ${getActionTypeName(action)}, dispatch?: SignalDispatch) => void
|
|
3490
|
+
`.raw;
|
|
3491
|
+
}
|
|
3492
|
+
function getOperationsInterfaceField(action, pascalCaseDocumentType) {
|
|
3493
|
+
return ts`
|
|
3494
|
+
${getActionOperationFieldName(action)}: ${getActionOperationFunction(action, pascalCaseDocumentType)}
|
|
3495
|
+
`.raw;
|
|
3496
|
+
}
|
|
3497
|
+
function getOperationsInterfaceFields(actions, pascalCaseDocumentType) {
|
|
3498
|
+
return actions.map((action) => getOperationsInterfaceField(action, pascalCaseDocumentType)).join(",");
|
|
3499
|
+
}
|
|
3500
|
+
const documentModelOperationsModuleOperationsFileTemplate = (v) => ts`
|
|
3501
|
+
import { type SignalDispatch } from 'document-model';
|
|
3502
|
+
import type {
|
|
3503
|
+
${getActionTypeImports(v.actions)}
|
|
3504
|
+
} from './actions.js';
|
|
3505
|
+
import type {
|
|
3506
|
+
${getActionOperationStateTypeImports(v.actions, v.pascalCaseDocumentType)}
|
|
3507
|
+
} from "../types.js";
|
|
3508
|
+
|
|
3509
|
+
export interface ${getOperationsInterfaceName(v.pascalCaseDocumentType, v.module)} {
|
|
3510
|
+
${getOperationsInterfaceFields(v.actions, v.pascalCaseDocumentType)}
|
|
3511
|
+
}
|
|
3512
|
+
`.raw;
|
|
3513
|
+
//#endregion
|
|
3514
|
+
//#region src/templates/document-model/gen/ph-factories.ts
|
|
3515
|
+
const documentModelPhFactoriesFileTemplate = (v) => ts`
|
|
3516
|
+
/**
|
|
3517
|
+
* Factory methods for creating ${v.phDocumentTypeName} instances
|
|
3518
|
+
*/
|
|
3519
|
+
import type {
|
|
3520
|
+
PHAuthState,
|
|
3521
|
+
PHDocumentState,
|
|
3522
|
+
PHBaseState,
|
|
3523
|
+
} from "document-model";
|
|
3524
|
+
import {
|
|
3525
|
+
createBaseState,
|
|
3526
|
+
defaultBaseState,
|
|
3527
|
+
} from "document-model";
|
|
3528
|
+
import type {
|
|
3529
|
+
${v.phDocumentTypeName},
|
|
3530
|
+
${v.globalStateName},
|
|
3531
|
+
${v.localStateName},
|
|
3532
|
+
${v.phStateName},
|
|
3533
|
+
} from "./types.js";
|
|
3534
|
+
import { utils } from "./utils.js";
|
|
3535
|
+
|
|
3536
|
+
export function defaultGlobalState(): ${v.globalStateName} {
|
|
3537
|
+
return ${v.initialGlobalState};
|
|
3538
|
+
}
|
|
3539
|
+
|
|
3540
|
+
export function defaultLocalState(): ${v.localStateName} {
|
|
3541
|
+
return ${v.initialLocalState};
|
|
3542
|
+
}
|
|
3543
|
+
|
|
3544
|
+
export function defaultPHState(): ${v.phStateName} {
|
|
3545
|
+
return {
|
|
3546
|
+
...defaultBaseState(),
|
|
3547
|
+
global: defaultGlobalState(),
|
|
3548
|
+
local: defaultLocalState(),
|
|
3549
|
+
};
|
|
3550
|
+
}
|
|
3551
|
+
|
|
3552
|
+
export function createGlobalState(
|
|
3553
|
+
state?: Partial<${v.globalStateName}>,
|
|
3554
|
+
): ${v.globalStateName} {
|
|
3555
|
+
return {
|
|
3556
|
+
...defaultGlobalState(),
|
|
3557
|
+
...(state || {}),
|
|
3558
|
+
} as ${v.globalStateName};
|
|
3559
|
+
}
|
|
3560
|
+
|
|
3561
|
+
export function createLocalState(
|
|
3562
|
+
state?: Partial<${v.localStateName}>,
|
|
3563
|
+
): ${v.localStateName} {
|
|
3564
|
+
return {
|
|
3565
|
+
...defaultLocalState(),
|
|
3566
|
+
...(state || {}),
|
|
3567
|
+
} as ${v.localStateName};
|
|
3568
|
+
}
|
|
3569
|
+
|
|
3570
|
+
export function createState(
|
|
3571
|
+
baseState?: Partial<PHBaseState>,
|
|
3572
|
+
globalState?: Partial<${v.globalStateName}>,
|
|
3573
|
+
localState?: Partial<${v.localStateName}>,
|
|
3574
|
+
): ${v.phStateName} {
|
|
3575
|
+
return {
|
|
3576
|
+
...createBaseState(baseState?.auth, baseState?.document),
|
|
3577
|
+
global: createGlobalState(globalState),
|
|
3578
|
+
local: createLocalState(localState),
|
|
3579
|
+
};
|
|
3580
|
+
}
|
|
3581
|
+
|
|
3582
|
+
/**
|
|
3583
|
+
* Creates a ${v.phDocumentTypeName} with custom global and local state
|
|
3584
|
+
* This properly handles the PHBaseState requirements while allowing
|
|
3585
|
+
* document-specific state to be set.
|
|
3586
|
+
*/
|
|
3587
|
+
export function create${v.phDocumentTypeName}(
|
|
3588
|
+
state?: Partial<{
|
|
3589
|
+
auth?: Partial<PHAuthState>;
|
|
3590
|
+
document?: Partial<PHDocumentState>;
|
|
3591
|
+
global?: Partial<${v.globalStateName}>;
|
|
3592
|
+
local?: Partial<${v.localStateName}>;
|
|
3593
|
+
}>,
|
|
3594
|
+
): ${v.phDocumentTypeName} {
|
|
3595
|
+
const document = utils.createDocument(
|
|
3596
|
+
state ? createState(
|
|
3597
|
+
createBaseState(state.auth, state.document),
|
|
3598
|
+
state.global,
|
|
3599
|
+
state.local,
|
|
3600
|
+
) : undefined
|
|
3601
|
+
);
|
|
3602
|
+
|
|
3603
|
+
return document;
|
|
3604
|
+
}
|
|
3605
|
+
`.raw;
|
|
3606
|
+
//#endregion
|
|
3607
|
+
//#region src/templates/document-model/gen/reducer.ts
|
|
3608
|
+
function makePascalCaseOperationName(operation) {
|
|
3609
|
+
if (!operation.name) throw new Error("Operation is missing name");
|
|
3610
|
+
return pascalCase(operation.name);
|
|
3611
|
+
}
|
|
3612
|
+
function makeCamelCaseOperationName(operation) {
|
|
3613
|
+
if (!operation.name) throw new Error("Operation is missing name");
|
|
3614
|
+
return camelCase(operation.name);
|
|
3615
|
+
}
|
|
3616
|
+
function makeConstantCaseOperationName(operation) {
|
|
3617
|
+
if (!operation.name) throw new Error("Operation is missing name");
|
|
3618
|
+
return constantCase(operation.name);
|
|
3619
|
+
}
|
|
3620
|
+
function makeOperationInputSchema(operation) {
|
|
3621
|
+
return `${makePascalCaseOperationName(operation)}InputSchema`;
|
|
3622
|
+
}
|
|
3623
|
+
function makeOperationInputSchemaImports(modules) {
|
|
3624
|
+
return `import { ${modules.flatMap((module) => module.operations.map(makeOperationInputSchema)).join(",\n")} } from "./schema/zod.js";`;
|
|
3625
|
+
}
|
|
3626
|
+
function makeModuleOperationsImport(module, camelCaseDocumentType) {
|
|
3627
|
+
return `import { ${camelCaseDocumentType}${pascalCase(module.name)}Operations } from "../src/reducers/${kebabCase(module.name)}.js";`;
|
|
3628
|
+
}
|
|
3629
|
+
function makeModulesOperationsImports(modules, camelCaseDocumentType) {
|
|
3630
|
+
return modules.map((module) => makeModuleOperationsImport(module, camelCaseDocumentType)).join("\n");
|
|
3631
|
+
}
|
|
3632
|
+
function makeOperationInputSchemaInvocation(operation) {
|
|
3633
|
+
const operationInputSchema = makeOperationInputSchema(operation);
|
|
3634
|
+
const constantCaseOperationName = makeConstantCaseOperationName(operation);
|
|
3635
|
+
if (operation.schema === null) return ts`
|
|
3636
|
+
if (Object.keys(action.input).length > 0) throw new Error("Expected empty input for action ${constantCaseOperationName}");
|
|
3637
|
+
`.raw;
|
|
3638
|
+
return ts`${operationInputSchema}().parse(action.input);`.raw;
|
|
3639
|
+
}
|
|
3640
|
+
function makeOperationsObjectName(module, camelCaseDocumentType) {
|
|
3641
|
+
return `${camelCaseDocumentType}${pascalCase(module.name)}Operations`;
|
|
3642
|
+
}
|
|
3643
|
+
function makeOperationName(operation) {
|
|
3644
|
+
return `${makeCamelCaseOperationName(operation)}Operation`;
|
|
3645
|
+
}
|
|
3646
|
+
function makeOperationInvocation(module, operation, camelCaseDocumentType) {
|
|
3647
|
+
return ts`
|
|
3648
|
+
${makeOperationsObjectName(module, camelCaseDocumentType)}.${makeOperationName(operation)}((state as any)[action.scope], action as any, dispatch);
|
|
3649
|
+
`.raw;
|
|
3650
|
+
}
|
|
3651
|
+
function makeModuleOperationCaseStatement(module, camelCaseDocumentType) {
|
|
3652
|
+
return module.operations.map((operation) => ts`
|
|
3653
|
+
case "${makeConstantCaseOperationName(operation)}": {
|
|
3654
|
+
${makeOperationInputSchemaInvocation(operation)}
|
|
3655
|
+
${makeOperationInvocation(module, operation, camelCaseDocumentType)}
|
|
3656
|
+
break;
|
|
3657
|
+
}
|
|
3658
|
+
`.raw);
|
|
3659
|
+
}
|
|
3660
|
+
function makeModuleOperationsCaseStatements(modules, camelCaseDocumentType) {
|
|
3661
|
+
return modules.map((module) => makeModuleOperationCaseStatement(module, camelCaseDocumentType).join("\n")).join("\n");
|
|
3662
|
+
}
|
|
3663
|
+
const documentModelGenReducerFileTemplate = (v) => ts`
|
|
3664
|
+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
3665
|
+
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
3666
|
+
import type { Reducer, StateReducer } from "document-model";
|
|
3667
|
+
import { isDocumentAction, createReducer } from "document-model";
|
|
3668
|
+
import type { ${v.phStateName} } from "${v.versionedDocumentModelPackageImportPath}";
|
|
3669
|
+
|
|
3670
|
+
${makeModulesOperationsImports(v.modules, v.camelCaseDocumentType)}
|
|
3671
|
+
|
|
3672
|
+
${makeOperationInputSchemaImports(v.modules)}
|
|
3673
|
+
|
|
3674
|
+
const stateReducer: StateReducer<${v.phStateName}> =
|
|
3675
|
+
(state, action, dispatch) => {
|
|
3676
|
+
if (isDocumentAction(action)) {
|
|
3677
|
+
return state;
|
|
3678
|
+
}
|
|
3679
|
+
switch (action.type) {
|
|
3680
|
+
${makeModuleOperationsCaseStatements(v.modules, v.camelCaseDocumentType)}
|
|
3681
|
+
default:
|
|
3682
|
+
return state;
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
|
|
3686
|
+
export const reducer: Reducer<${v.phStateName}> = createReducer(stateReducer);
|
|
3687
|
+
`.raw;
|
|
3688
|
+
//#endregion
|
|
3689
|
+
//#region src/templates/document-model/gen/schema/index.ts
|
|
3690
|
+
const documentModelSchemaIndexTemplate = ts`
|
|
3691
|
+
export * from "./types.js";
|
|
3692
|
+
export * from "./zod.js";
|
|
3693
|
+
`.raw;
|
|
3694
|
+
//#endregion
|
|
3695
|
+
//#region src/templates/document-model/gen/types.ts
|
|
3696
|
+
function buildEmptyLocalStateType(hasLocalSchema, localStateName) {
|
|
3697
|
+
if (hasLocalSchema) return "";
|
|
3698
|
+
return `type ${localStateName} = Record<PropertyKey, never>;`;
|
|
3699
|
+
}
|
|
3700
|
+
function buildLocalStateTypeImport(hasLocalSchema, localStateName) {
|
|
3701
|
+
if (!hasLocalSchema) return "";
|
|
3702
|
+
return localStateName;
|
|
3703
|
+
}
|
|
3704
|
+
const documentModelGenTypesTemplate = (v) => ts`
|
|
3705
|
+
import type { PHDocument, PHBaseState } from 'document-model';
|
|
3706
|
+
import type { ${v.actionTypeName} } from './actions.js';
|
|
3707
|
+
import type {
|
|
3708
|
+
${v.stateName} as ${v.globalStateName},
|
|
3709
|
+
${buildLocalStateTypeImport(v.hasLocalSchema, v.localStateName)}
|
|
3710
|
+
} from './schema/types.js';
|
|
3711
|
+
|
|
3712
|
+
${buildEmptyLocalStateType(v.hasLocalSchema, v.localStateName)}
|
|
3713
|
+
|
|
3714
|
+
type ${v.phStateName} = PHBaseState & {
|
|
3715
|
+
global: ${v.globalStateName};
|
|
3716
|
+
local: ${v.localStateName};
|
|
3717
|
+
};
|
|
3718
|
+
type ${v.phDocumentTypeName} = PHDocument<${v.phStateName}>;
|
|
3719
|
+
|
|
3720
|
+
export * from './schema/types.js';
|
|
3721
|
+
|
|
3722
|
+
export type {
|
|
3723
|
+
${v.globalStateName},
|
|
3724
|
+
${v.localStateName},
|
|
3725
|
+
${v.phStateName},
|
|
3726
|
+
${v.actionTypeName},
|
|
3727
|
+
${v.phDocumentTypeName},
|
|
3728
|
+
};
|
|
3729
|
+
`.raw;
|
|
3730
|
+
//#endregion
|
|
3731
|
+
//#region src/templates/document-model/gen/utils.ts
|
|
3732
|
+
const documentModelGenUtilsTemplate = (v) => ts`
|
|
3733
|
+
import type {
|
|
3734
|
+
DocumentModelUtils,
|
|
3735
|
+
} from "document-model";
|
|
3736
|
+
import {
|
|
3737
|
+
baseCreateDocument,
|
|
3738
|
+
baseSaveToFileHandle,
|
|
3739
|
+
baseLoadFromInput,
|
|
3740
|
+
defaultBaseState,
|
|
3741
|
+
generateId,
|
|
3742
|
+
} from "document-model";
|
|
3743
|
+
import { reducer } from './reducer.js';
|
|
3744
|
+
import { ${v.documentTypeVariableName} } from "./document-type.js";
|
|
3745
|
+
import {
|
|
3746
|
+
${v.assertIsPhDocumentOfTypeFunctionName},
|
|
3747
|
+
${v.assertIsPhStateOfTypeFunctionName},
|
|
3748
|
+
${v.isPhDocumentOfTypeFunctionName},
|
|
3749
|
+
${v.isPhStateOfTypeFunctionName},
|
|
3750
|
+
} from "./document-schema.js";
|
|
3751
|
+
import type { ${v.globalStateName}, ${v.localStateName}, ${v.phStateName} } from './types.js';
|
|
3752
|
+
|
|
3753
|
+
export const initialGlobalState: ${v.globalStateName} = ${v.initialGlobalState};
|
|
3754
|
+
export const initialLocalState: ${v.localStateName} = ${v.initialLocalState};
|
|
3755
|
+
|
|
3756
|
+
export const utils: DocumentModelUtils<${v.phStateName}> = {
|
|
3757
|
+
fileExtension: "${v.fileExtension}",
|
|
3758
|
+
createState(state) {
|
|
3759
|
+
return { ...defaultBaseState(), global: { ...initialGlobalState, ...state?.global }, local: { ...initialLocalState, ...state?.local } };
|
|
3760
|
+
},
|
|
3761
|
+
createDocument(state) {
|
|
3762
|
+
const document = baseCreateDocument(
|
|
3763
|
+
utils.createState,
|
|
3764
|
+
state
|
|
3765
|
+
);
|
|
3766
|
+
|
|
3767
|
+
document.header.documentType = ${v.documentTypeVariableName};
|
|
3768
|
+
|
|
3769
|
+
// for backwards compatibility, but this is NOT a valid signed document id
|
|
3770
|
+
document.header.id = generateId();
|
|
3771
|
+
|
|
3772
|
+
return document;
|
|
3773
|
+
},
|
|
3774
|
+
saveToFileHandle(document, input) {
|
|
3775
|
+
return baseSaveToFileHandle(document, input);
|
|
3776
|
+
},
|
|
3777
|
+
loadFromInput(input) {
|
|
3778
|
+
return baseLoadFromInput(input, reducer);
|
|
3779
|
+
},
|
|
3780
|
+
isStateOfType(state) {
|
|
3781
|
+
return ${v.isPhStateOfTypeFunctionName}(state);
|
|
3782
|
+
},
|
|
3783
|
+
assertIsStateOfType(state) {
|
|
3784
|
+
return ${v.assertIsPhStateOfTypeFunctionName}(state);
|
|
3785
|
+
},
|
|
3786
|
+
isDocumentOfType(document) {
|
|
3787
|
+
return ${v.isPhDocumentOfTypeFunctionName}(document);
|
|
3788
|
+
},
|
|
3789
|
+
assertIsDocumentOfType(document) {
|
|
3790
|
+
return ${v.assertIsPhDocumentOfTypeFunctionName}(document);
|
|
3791
|
+
},
|
|
3792
|
+
};
|
|
3793
|
+
`.raw;
|
|
3794
|
+
//#endregion
|
|
3795
|
+
//#region src/templates/document-model/hooks.ts
|
|
3796
|
+
const documentModelHooksFileTemplate = (v) => ts`
|
|
3797
|
+
|
|
3798
|
+
import type { DocumentDispatch } from "@powerhousedao/reactor-browser";
|
|
3799
|
+
import {
|
|
3800
|
+
useDocumentById,
|
|
3801
|
+
useDocumentsInSelectedDrive,
|
|
3802
|
+
useDocumentsInSelectedFolder,
|
|
3803
|
+
useSelectedDocument,
|
|
3804
|
+
} from "@powerhousedao/reactor-browser";
|
|
3805
|
+
import type {
|
|
3806
|
+
${v.actionTypeName},
|
|
3807
|
+
${v.phDocumentTypeName},
|
|
3808
|
+
} from "${v.versionedDocumentModelPackageImportPath}";
|
|
3809
|
+
import {
|
|
3810
|
+
${v.assertIsPhDocumentOfTypeFunctionName},
|
|
3811
|
+
${v.isPhDocumentOfTypeFunctionName}
|
|
3812
|
+
} from "./gen/document-schema.js";
|
|
3813
|
+
|
|
3814
|
+
/** Hook to get a ${v.pascalCaseDocumentType} document by its id */
|
|
3815
|
+
export function ${v.useByIdHookName}(
|
|
3816
|
+
documentId: string | null | undefined,
|
|
3817
|
+
):
|
|
3818
|
+
| [${v.phDocumentTypeName}, DocumentDispatch<${v.actionTypeName}>]
|
|
3819
|
+
| [undefined, undefined] {
|
|
3820
|
+
const [document, dispatch] = useDocumentById(documentId);
|
|
3821
|
+
if (!${v.isPhDocumentOfTypeFunctionName}(document)) return [undefined, undefined];
|
|
3822
|
+
return [document, dispatch];
|
|
3823
|
+
}
|
|
3824
|
+
|
|
3825
|
+
/** Hook to get the selected ${v.pascalCaseDocumentType} document */
|
|
3826
|
+
export function ${v.useSelectedHookName}():
|
|
3827
|
+
| [${v.phDocumentTypeName}, DocumentDispatch<${v.actionTypeName}>] {
|
|
3828
|
+
const [document, dispatch] = useSelectedDocument();
|
|
3829
|
+
|
|
3830
|
+
${v.assertIsPhDocumentOfTypeFunctionName}(document);
|
|
3831
|
+
return [document, dispatch] as const;
|
|
3832
|
+
}
|
|
3833
|
+
|
|
3834
|
+
/** Hook to get all ${v.pascalCaseDocumentType} documents in the selected drive */
|
|
3835
|
+
export function ${v.useInSelectedDriveHookName}() {
|
|
3836
|
+
const documentsInSelectedDrive = useDocumentsInSelectedDrive();
|
|
3837
|
+
return documentsInSelectedDrive?.filter(${v.isPhDocumentOfTypeFunctionName});
|
|
3838
|
+
}
|
|
3839
|
+
|
|
3840
|
+
/** Hook to get all ${v.pascalCaseDocumentType} documents in the selected folder */
|
|
3841
|
+
export function ${v.useInSelectedFolderHookName}() {
|
|
3842
|
+
const documentsInSelectedFolder = useDocumentsInSelectedFolder();
|
|
3843
|
+
return documentsInSelectedFolder?.filter(${v.isPhDocumentOfTypeFunctionName});
|
|
3844
|
+
}
|
|
3845
|
+
`.raw;
|
|
3846
|
+
//#endregion
|
|
3847
|
+
//#region src/templates/document-model/index.ts
|
|
3848
|
+
const documentModelIndexTemplate = ts`
|
|
3849
|
+
export * from "./gen/index.js";
|
|
3850
|
+
export * from "./src/index.js";
|
|
3851
|
+
export * from "./hooks.js";
|
|
3852
|
+
export { actions } from "./actions.js";
|
|
3853
|
+
export { utils } from "./utils.js";
|
|
3854
|
+
`.raw;
|
|
3855
|
+
//#endregion
|
|
3856
|
+
//#region src/templates/document-model/module.ts
|
|
3857
|
+
function documentModelModuleFileTemplate({ phStateName, pascalCaseDocumentType, version }) {
|
|
3858
|
+
return ts`
|
|
3859
|
+
import type { DocumentModelModule } from "document-model";
|
|
3860
|
+
import { createState, defaultBaseState } from "document-model";
|
|
3861
|
+
import type { ${phStateName} } from "./gen/types.js";
|
|
3862
|
+
import { documentModel } from "./gen/document-model.js";
|
|
3863
|
+
import { reducer } from "./gen/reducer.js";
|
|
3864
|
+
import { actions } from "./actions.js";
|
|
3865
|
+
import { utils } from "./utils.js";
|
|
3866
|
+
|
|
3867
|
+
/** Document model module for the ${pascalCaseDocumentType} document type */
|
|
3868
|
+
export const ${pascalCaseDocumentType}: DocumentModelModule<${phStateName}> = {
|
|
3869
|
+
version: ${version},
|
|
3870
|
+
reducer,
|
|
3871
|
+
actions,
|
|
3872
|
+
utils,
|
|
3873
|
+
documentModel: createState(defaultBaseState(), documentModel),
|
|
3874
|
+
};
|
|
3875
|
+
`.raw;
|
|
3876
|
+
}
|
|
3877
|
+
//#endregion
|
|
3878
|
+
//#region src/templates/document-model/src/index.ts
|
|
3879
|
+
const documentModelSrcIndexFileTemplate = ts`
|
|
3880
|
+
export * from "./utils.js";
|
|
3881
|
+
`.raw;
|
|
3882
|
+
//#endregion
|
|
3883
|
+
//#region src/templates/document-model/src/utils.ts
|
|
3884
|
+
const documentModelSrcUtilsTemplate = ts`
|
|
3885
|
+
export {};
|
|
3886
|
+
`.raw;
|
|
3887
|
+
//#endregion
|
|
3888
|
+
//#region src/templates/document-model/tests/document-model.test.ts
|
|
3889
|
+
const documentModelTestFileTemplate = (v) => ts`
|
|
3890
|
+
/**
|
|
3891
|
+
* This is a scaffold file meant for customization:
|
|
3892
|
+
* - change it by adding new tests or modifying the existing ones
|
|
3893
|
+
*/
|
|
3894
|
+
/**
|
|
3895
|
+
* This is a scaffold file meant for customization:
|
|
3896
|
+
* - change it by adding new tests or modifying the existing ones
|
|
3897
|
+
*/
|
|
3898
|
+
|
|
3899
|
+
import { describe, it, expect } from "vitest";
|
|
3900
|
+
import {
|
|
3901
|
+
utils,
|
|
3902
|
+
initialGlobalState,
|
|
3903
|
+
initialLocalState,
|
|
3904
|
+
${v.documentTypeVariableName},
|
|
3905
|
+
${v.isPhDocumentOfTypeFunctionName},
|
|
3906
|
+
${v.assertIsPhDocumentOfTypeFunctionName},
|
|
3907
|
+
${v.isPhStateOfTypeFunctionName},
|
|
3908
|
+
${v.assertIsPhStateOfTypeFunctionName},
|
|
3909
|
+
} from "${v.versionedDocumentModelPackageImportPath}";
|
|
3910
|
+
import { ZodError } from "zod";
|
|
3911
|
+
|
|
3912
|
+
describe("${v.pascalCaseDocumentType} Document Model", () => {
|
|
3913
|
+
it("should create a new ${v.pascalCaseDocumentType} document", () => {
|
|
3914
|
+
const document = utils.createDocument();
|
|
3915
|
+
|
|
3916
|
+
expect(document).toBeDefined();
|
|
3917
|
+
expect(document.header.documentType).toBe(${v.documentTypeVariableName});
|
|
3918
|
+
});
|
|
3919
|
+
|
|
3920
|
+
it("should create a new ${v.pascalCaseDocumentType} document with a valid initial state", () => {
|
|
3921
|
+
const document = utils.createDocument();
|
|
3922
|
+
expect(document.state.global).toStrictEqual(initialGlobalState);
|
|
3923
|
+
expect(document.state.local).toStrictEqual(initialLocalState);
|
|
3924
|
+
expect(${v.isPhDocumentOfTypeFunctionName}(document)).toBe(true);
|
|
3925
|
+
expect(${v.isPhStateOfTypeFunctionName}(document.state)).toBe(true);
|
|
3926
|
+
});
|
|
3927
|
+
it("should reject a document that is not a ${v.pascalCaseDocumentType} document", () => {
|
|
3928
|
+
const wrongDocumentType = utils.createDocument();
|
|
3929
|
+
wrongDocumentType.header.documentType = "the-wrong-thing-1234";
|
|
3930
|
+
try {
|
|
3931
|
+
expect(${v.assertIsPhDocumentOfTypeFunctionName}(wrongDocumentType)).toThrow();
|
|
3932
|
+
expect(${v.isPhDocumentOfTypeFunctionName}(wrongDocumentType)).toBe(false);
|
|
3933
|
+
} catch (error) {
|
|
3934
|
+
expect(error).toBeInstanceOf(ZodError);
|
|
3935
|
+
}
|
|
3936
|
+
});
|
|
3937
|
+
const wrongState = utils.createDocument();
|
|
3938
|
+
// @ts-expect-error - we are testing the error case
|
|
3939
|
+
wrongState.state.global = {
|
|
3940
|
+
...{ notWhat: "you want" },
|
|
3941
|
+
};
|
|
3942
|
+
try {
|
|
3943
|
+
expect(${v.isPhStateOfTypeFunctionName}(wrongState.state)).toBe(false);
|
|
3944
|
+
expect(${v.assertIsPhStateOfTypeFunctionName}(wrongState.state)).toThrow();
|
|
3945
|
+
expect(${v.isPhDocumentOfTypeFunctionName}(wrongState)).toBe(false);
|
|
3946
|
+
expect(${v.assertIsPhDocumentOfTypeFunctionName}(wrongState)).toThrow();
|
|
3947
|
+
} catch (error) {
|
|
3948
|
+
expect(error).toBeInstanceOf(ZodError);
|
|
3949
|
+
}
|
|
3950
|
+
|
|
3951
|
+
const wrongInitialState = utils.createDocument();
|
|
3952
|
+
// @ts-expect-error - we are testing the error case
|
|
3953
|
+
wrongInitialState.initialState.global = {
|
|
3954
|
+
...{ notWhat: "you want" },
|
|
3955
|
+
};
|
|
3956
|
+
try {
|
|
3957
|
+
expect(${v.isPhStateOfTypeFunctionName}(wrongInitialState.state)).toBe(false);
|
|
3958
|
+
expect(${v.assertIsPhStateOfTypeFunctionName}(wrongInitialState.state)).toThrow();
|
|
3959
|
+
expect(${v.isPhDocumentOfTypeFunctionName}(wrongInitialState)).toBe(false);
|
|
3960
|
+
expect(${v.assertIsPhDocumentOfTypeFunctionName}(wrongInitialState)).toThrow();
|
|
3961
|
+
} catch (error) {
|
|
3962
|
+
expect(error).toBeInstanceOf(ZodError);
|
|
3963
|
+
}
|
|
3964
|
+
|
|
3965
|
+
const missingIdInHeader = utils.createDocument();
|
|
3966
|
+
// @ts-expect-error - we are testing the error case
|
|
3967
|
+
delete missingIdInHeader.header.id;
|
|
3968
|
+
try {
|
|
3969
|
+
expect(${v.isPhDocumentOfTypeFunctionName}(missingIdInHeader)).toBe(false);
|
|
3970
|
+
expect(${v.assertIsPhDocumentOfTypeFunctionName}(missingIdInHeader)).toThrow();
|
|
3971
|
+
} catch (error) {
|
|
3972
|
+
expect(error).toBeInstanceOf(ZodError);
|
|
3973
|
+
}
|
|
3974
|
+
|
|
3975
|
+
const missingNameInHeader = utils.createDocument();
|
|
3976
|
+
// @ts-expect-error - we are testing the error case
|
|
3977
|
+
delete missingNameInHeader.header.name;
|
|
3978
|
+
try {
|
|
3979
|
+
expect(${v.isPhDocumentOfTypeFunctionName}(missingNameInHeader)).toBe(false);
|
|
3980
|
+
expect(${v.assertIsPhDocumentOfTypeFunctionName}(missingNameInHeader)).toThrow();
|
|
3981
|
+
} catch (error) {
|
|
3982
|
+
expect(error).toBeInstanceOf(ZodError);
|
|
3983
|
+
}
|
|
3984
|
+
|
|
3985
|
+
const missingCreatedAtUtcIsoInHeader = utils.createDocument();
|
|
3986
|
+
// @ts-expect-error - we are testing the error case
|
|
3987
|
+
delete missingCreatedAtUtcIsoInHeader.header.createdAtUtcIso;
|
|
3988
|
+
try {
|
|
3989
|
+
expect(${v.isPhDocumentOfTypeFunctionName}(missingCreatedAtUtcIsoInHeader)).toBe(false);
|
|
3990
|
+
expect(${v.assertIsPhDocumentOfTypeFunctionName}(missingCreatedAtUtcIsoInHeader)).toThrow();
|
|
3991
|
+
} catch (error) {
|
|
3992
|
+
expect(error).toBeInstanceOf(ZodError);
|
|
3993
|
+
}
|
|
3994
|
+
|
|
3995
|
+
const missingLastModifiedAtUtcIsoInHeader = utils.createDocument();
|
|
3996
|
+
// @ts-expect-error - we are testing the error case
|
|
3997
|
+
delete missingLastModifiedAtUtcIsoInHeader.header.lastModifiedAtUtcIso;
|
|
3998
|
+
try {
|
|
3999
|
+
expect(${v.isPhDocumentOfTypeFunctionName}(missingLastModifiedAtUtcIsoInHeader)).toBe(false);
|
|
4000
|
+
expect(
|
|
4001
|
+
${v.assertIsPhDocumentOfTypeFunctionName}(missingLastModifiedAtUtcIsoInHeader),
|
|
4002
|
+
).toThrow();
|
|
4003
|
+
} catch (error) {
|
|
4004
|
+
expect(error).toBeInstanceOf(ZodError);
|
|
4005
|
+
}
|
|
4006
|
+
});
|
|
4007
|
+
`.raw;
|
|
4008
|
+
//#endregion
|
|
4009
|
+
//#region src/templates/document-model/tests/module.test.ts
|
|
4010
|
+
function makeModuleOperationsTypeName(module) {
|
|
4011
|
+
return `${pascalCase(module.name)}Operations`;
|
|
4012
|
+
}
|
|
4013
|
+
function makeCamelCaseActionNamesForImport(actions) {
|
|
4014
|
+
return actions.map((a) => camelCase(a.name));
|
|
4015
|
+
}
|
|
4016
|
+
function makeActionInputSchemasForImport(actions) {
|
|
4017
|
+
return actions.map((a) => `${pascalCase(a.name)}InputSchema`);
|
|
4018
|
+
}
|
|
4019
|
+
function makeTestCaseForAction(action, isPhDocumentOfTypeFunctionName) {
|
|
4020
|
+
const camelCaseActionName = camelCase(action.name);
|
|
4021
|
+
const pascalCaseActionName = pascalCase(action.name);
|
|
4022
|
+
const constantCaseActionName = constantCase(action.name);
|
|
4023
|
+
const actionInputSchemaName = `${pascalCaseActionName}InputSchema`;
|
|
4024
|
+
const scope = action.scope;
|
|
4025
|
+
return ts`
|
|
4026
|
+
it('should handle ${camelCaseActionName} operation', () => {
|
|
4027
|
+
const document = utils.createDocument();
|
|
4028
|
+
const input = generateMock(
|
|
4029
|
+
${actionInputSchemaName}(),
|
|
4030
|
+
);
|
|
4031
|
+
|
|
4032
|
+
const updatedDocument = reducer(
|
|
4033
|
+
document,
|
|
4034
|
+
${camelCaseActionName}(input),
|
|
4035
|
+
);
|
|
4036
|
+
|
|
4037
|
+
expect(${isPhDocumentOfTypeFunctionName}(updatedDocument)).toBe(true);
|
|
4038
|
+
expect(updatedDocument.operations.${scope}).toHaveLength(1);
|
|
4039
|
+
expect(updatedDocument.operations.${scope}[0].action.type).toBe(
|
|
4040
|
+
"${constantCaseActionName}",
|
|
4041
|
+
);
|
|
4042
|
+
expect(updatedDocument.operations.${scope}[0].action.input).toStrictEqual(input);
|
|
4043
|
+
expect(updatedDocument.operations.${scope}[0].index).toEqual(0);
|
|
4044
|
+
});
|
|
4045
|
+
`.raw;
|
|
4046
|
+
}
|
|
4047
|
+
function makeActionImportNames(v) {
|
|
4048
|
+
const actionNames = makeCamelCaseActionNamesForImport(v.actions);
|
|
4049
|
+
const inputSchemaNames = makeActionInputSchemasForImport(v.actions);
|
|
4050
|
+
return [
|
|
4051
|
+
"reducer",
|
|
4052
|
+
"utils",
|
|
4053
|
+
v.isPhDocumentOfTypeFunctionName,
|
|
4054
|
+
...actionNames,
|
|
4055
|
+
...inputSchemaNames
|
|
4056
|
+
];
|
|
4057
|
+
}
|
|
4058
|
+
function makeActionsImports(v) {
|
|
4059
|
+
return ts`
|
|
4060
|
+
import {
|
|
4061
|
+
${makeActionImportNames(v).join("\n")}
|
|
4062
|
+
} from "${v.versionedDocumentModelPackageImportPath}";
|
|
4063
|
+
`.raw;
|
|
4064
|
+
}
|
|
4065
|
+
function makeTestCasesForActions(actions, isPhDocumentOfTypeFunctionName) {
|
|
4066
|
+
return actions.map((action) => makeTestCaseForAction(action, isPhDocumentOfTypeFunctionName)).join("\n\n");
|
|
4067
|
+
}
|
|
4068
|
+
const documentModelOperationsModuleTestFileTemplate = (v) => ts`
|
|
4069
|
+
/**
|
|
4070
|
+
* This is a scaffold file meant for customization:
|
|
4071
|
+
* - change it by adding new tests or modifying the existing ones
|
|
4072
|
+
*/
|
|
4073
|
+
|
|
4074
|
+
import { describe, it, expect } from 'vitest';
|
|
4075
|
+
import { generateMock } from '@powerhousedao/common';
|
|
4076
|
+
import {
|
|
4077
|
+
reducer,
|
|
4078
|
+
utils,
|
|
4079
|
+
${v.isPhDocumentOfTypeFunctionName},
|
|
4080
|
+
${makeCamelCaseActionNamesForImport(v.actions)},
|
|
4081
|
+
${makeActionInputSchemasForImport(v.actions)},
|
|
4082
|
+
} from "${v.versionedDocumentModelPackageImportPath}";
|
|
4083
|
+
|
|
4084
|
+
describe("${makeModuleOperationsTypeName(v.module)}", () => {
|
|
4085
|
+
${makeTestCasesForActions(v.actions, v.isPhDocumentOfTypeFunctionName)}
|
|
4086
|
+
});
|
|
4087
|
+
|
|
4088
|
+
`.raw;
|
|
4089
|
+
//#endregion
|
|
4090
|
+
//#region src/templates/document-model/upgrades/upgrade-manifest.ts
|
|
4091
|
+
const upgradeManifestTemplate = (v) => ts`
|
|
4092
|
+
import type { UpgradeManifest } from "document-model";
|
|
4093
|
+
import { latestVersion, supportedVersions } from "./versions.js";
|
|
4094
|
+
|
|
4095
|
+
export const ${v.upgradeManifestName}: UpgradeManifest<typeof supportedVersions> = {
|
|
4096
|
+
documentType: "${v.documentModelId}",
|
|
4097
|
+
latestVersion,
|
|
4098
|
+
supportedVersions,
|
|
4099
|
+
upgrades: {},
|
|
4100
|
+
};
|
|
4101
|
+
`.raw;
|
|
4102
|
+
//#endregion
|
|
4103
|
+
//#region src/templates/document-model/upgrades/upgrade-transition.ts
|
|
4104
|
+
const upgradeTransitionTemplate = (v) => ts`
|
|
4105
|
+
import type { Action, PHDocument, UpgradeTransition } from "document-model";
|
|
4106
|
+
import type { ${v.phStateName} as StateV${v.previousVersion} } from "${v.documentModelPackageImportPath}/v${v.previousVersion}";
|
|
4107
|
+
import type { ${v.phStateName} as StateV${v.version} } from "${v.documentModelPackageImportPath}/v${v.version}";
|
|
4108
|
+
|
|
4109
|
+
function upgradeReducer(
|
|
4110
|
+
document: PHDocument<StateV${v.previousVersion}>,
|
|
4111
|
+
action: Action,
|
|
4112
|
+
): PHDocument<StateV${v.version}> {
|
|
4113
|
+
return {
|
|
4114
|
+
...document,
|
|
4115
|
+
};
|
|
4116
|
+
}
|
|
4117
|
+
|
|
4118
|
+
export const v${v.version}: UpgradeTransition = {
|
|
4119
|
+
toVersion: ${v.version},
|
|
4120
|
+
upgradeReducer,
|
|
4121
|
+
description: "",
|
|
4122
|
+
};
|
|
4123
|
+
`.raw;
|
|
4124
|
+
//#endregion
|
|
4125
|
+
//#region src/templates/document-model/utils.ts
|
|
4126
|
+
const documentModelUtilsTemplate = ({ phStateName, pascalCaseDocumentType }) => ts`
|
|
4127
|
+
import type { DocumentModelUtils } from "document-model";
|
|
4128
|
+
import type { ${phStateName} } from "./gen/types.js";
|
|
4129
|
+
import { utils as genUtils } from "./gen/utils.js";
|
|
4130
|
+
import * as customUtils from "./src/utils.js";
|
|
4131
|
+
|
|
4132
|
+
/** Utils for the ${pascalCaseDocumentType} document model */
|
|
4133
|
+
export const utils: DocumentModelUtils<${phStateName}> = { ...genUtils, ...customUtils };
|
|
4134
|
+
`.raw;
|
|
4135
|
+
//#endregion
|
|
4136
|
+
//#region src/templates/processors/utils.ts
|
|
4137
|
+
function getDocumentType(documentTypes) {
|
|
4138
|
+
if (!documentTypes.length) return `"*"`;
|
|
4139
|
+
return documentTypes.map((type) => `"${type}"`).join(", ");
|
|
4140
|
+
}
|
|
4141
|
+
//#endregion
|
|
4142
|
+
//#region src/templates/processors/analytics/factory.ts
|
|
4143
|
+
const analyticsFactoryTemplate = (v) => ts`
|
|
4144
|
+
import type {
|
|
4145
|
+
ProcessorApp,
|
|
4146
|
+
ProcessorFactoryBuilder,
|
|
4147
|
+
ProcessorRecord,
|
|
4148
|
+
IProcessorHostModule,
|
|
4149
|
+
} from "@powerhousedao/reactor-browser";
|
|
4150
|
+
import { type PHDocumentHeader } from "document-model";
|
|
4151
|
+
import { ${v.pascalCaseName} } from "./processor.js";
|
|
4152
|
+
|
|
4153
|
+
export const ${v.camelCaseName}FactoryBuilder: ProcessorFactoryBuilder = (module: IProcessorHostModule) => async (driveHeader: PHDocumentHeader, processorApp?: ProcessorApp) => {
|
|
4154
|
+
return [
|
|
4155
|
+
{
|
|
4156
|
+
processor: new ${v.pascalCaseName}(module.analyticsStore),
|
|
4157
|
+
filter: {
|
|
4158
|
+
branch: ["main"],
|
|
4159
|
+
documentId: ["*"],
|
|
4160
|
+
scope: ["*"],
|
|
4161
|
+
documentType: [${getDocumentType(v.documentTypes)}],
|
|
4162
|
+
},
|
|
4163
|
+
},
|
|
4164
|
+
];
|
|
4165
|
+
}
|
|
4166
|
+
`.raw;
|
|
4167
|
+
//#endregion
|
|
4168
|
+
//#region src/templates/processors/analytics/index.ts
|
|
4169
|
+
const analyticsIndexTemplate = ts`
|
|
4170
|
+
export * from "./factory.js";
|
|
4171
|
+
export * from "./processor.js";
|
|
4172
|
+
`.raw;
|
|
4173
|
+
//#endregion
|
|
4174
|
+
//#region src/templates/processors/analytics/processor.ts
|
|
4175
|
+
const analyticsProcessorTemplate = (v) => ts`
|
|
4176
|
+
import type { AnalyticsSeriesInput, AnalyticsPath, IAnalyticsStore } from "@powerhousedao/analytics-engine-core";
|
|
4177
|
+
import type { OperationWithContext, IProcessor } from "@powerhousedao/reactor-browser";
|
|
4178
|
+
|
|
4179
|
+
export class ${v.pascalCaseName} implements IProcessor {
|
|
4180
|
+
private readonly NAMESPACE = "${v.pascalCaseName}";
|
|
4181
|
+
|
|
4182
|
+
private readonly inputs: AnalyticsSeriesInput[] = [];
|
|
4183
|
+
|
|
4184
|
+
constructor(private readonly analyticsStore: IAnalyticsStore) {
|
|
4185
|
+
//
|
|
4186
|
+
}
|
|
4187
|
+
|
|
4188
|
+
onOperations(operations: OperationWithContext[]): Promise<void> {
|
|
4189
|
+
return Promise.resolve();
|
|
4190
|
+
}
|
|
4191
|
+
|
|
4192
|
+
onDisconnect(): Promise<void> {
|
|
4193
|
+
return Promise.resolve();
|
|
4194
|
+
}
|
|
4195
|
+
|
|
4196
|
+
private async clearSource(source: AnalyticsPath) {
|
|
4197
|
+
try {
|
|
4198
|
+
await this.analyticsStore.clearSeriesBySource(source, true);
|
|
4199
|
+
} catch (e) {
|
|
4200
|
+
console.error(e);
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
}
|
|
4204
|
+
`.raw;
|
|
4205
|
+
//#endregion
|
|
4206
|
+
//#region src/templates/processors/factory-builders.ts
|
|
4207
|
+
const factoryBuildersTemplate = ts`
|
|
4208
|
+
import type { ProcessorFactoryBuilder } from "@powerhousedao/reactor";
|
|
4209
|
+
|
|
4210
|
+
export const processorFactoryBuilders: ProcessorFactoryBuilder[] = [];
|
|
4211
|
+
`.raw;
|
|
4212
|
+
//#endregion
|
|
4213
|
+
//#region src/templates/processors/factory.ts
|
|
4214
|
+
const processorsFactoryTemplate = ts`
|
|
4215
|
+
/**
|
|
4216
|
+
* This file aggregates all processor factories
|
|
4217
|
+
* Auto-generated by codegen - DO NOT EDIT MANUALLY
|
|
4218
|
+
*/
|
|
4219
|
+
|
|
4220
|
+
import type {
|
|
4221
|
+
IProcessorHostModule,
|
|
4222
|
+
ProcessorRecord,
|
|
4223
|
+
} from "@powerhousedao/reactor-browser";
|
|
4224
|
+
import type { PHDocumentHeader } from "document-model";
|
|
4225
|
+
|
|
4226
|
+
export const processorFactory = async (module: IProcessorHostModule) => {
|
|
4227
|
+
const { processorFactoryBuilders } =
|
|
4228
|
+
module.processorApp === "connect"
|
|
4229
|
+
? await import("./connect.js")
|
|
4230
|
+
: await import("./switchboard.js");
|
|
4231
|
+
|
|
4232
|
+
const factories = await Promise.all(
|
|
4233
|
+
processorFactoryBuilders.map(
|
|
4234
|
+
async (buildFactory) => await buildFactory(module),
|
|
4235
|
+
),
|
|
4236
|
+
);
|
|
4237
|
+
|
|
4238
|
+
// Return the inner function that will be called for each drive
|
|
4239
|
+
return async (driveHeader: PHDocumentHeader): Promise<ProcessorRecord[]> => {
|
|
4240
|
+
const processors: ProcessorRecord[] = [];
|
|
4241
|
+
|
|
4242
|
+
// Call each cached factory with the driveHeader
|
|
4243
|
+
for (const factory of factories) {
|
|
4244
|
+
const factoryProcessors = await factory(driveHeader, module.processorApp);
|
|
4245
|
+
processors.push(...factoryProcessors);
|
|
4246
|
+
}
|
|
4247
|
+
|
|
4248
|
+
return processors;
|
|
4249
|
+
};
|
|
4250
|
+
};
|
|
4251
|
+
`.raw;
|
|
4252
|
+
//#endregion
|
|
4253
|
+
//#region src/templates/processors/index.ts
|
|
4254
|
+
const processorsIndexTemplate = ts`
|
|
4255
|
+
/**
|
|
4256
|
+
* Processor exports
|
|
4257
|
+
* This file is auto-generated and updated by codegen
|
|
4258
|
+
*/
|
|
4259
|
+
|
|
4260
|
+
export { processorFactory } from "./factory.js";
|
|
4261
|
+
`.raw;
|
|
4262
|
+
//#endregion
|
|
4263
|
+
//#region src/templates/processors/relational-db/factory.ts
|
|
4264
|
+
const relationalDbFactoryTemplate = (v) => ts`
|
|
4265
|
+
import type {
|
|
4266
|
+
IProcessorHostModule,
|
|
4267
|
+
ProcessorApp,
|
|
4268
|
+
ProcessorFactoryBuilder,
|
|
4269
|
+
ProcessorFilter,
|
|
4270
|
+
ProcessorRecord,
|
|
4271
|
+
} from "@powerhousedao/reactor-browser"
|
|
4272
|
+
import type { PHDocumentHeader } from "document-model";
|
|
4273
|
+
import { ${v.pascalCaseName} } from "./processor.js";
|
|
4274
|
+
|
|
4275
|
+
export const ${v.camelCaseName}FactoryBuilder: ProcessorFactoryBuilder = (module: IProcessorHostModule) => async (driveHeader: PHDocumentHeader, processorApp?: ProcessorApp) => {
|
|
4276
|
+
// Create a namespace for the processor and the provided drive id
|
|
4277
|
+
const namespace = ${v.pascalCaseName}.getNamespace(driveHeader.id);
|
|
4278
|
+
|
|
4279
|
+
// Create a namespaced db for the processor
|
|
4280
|
+
const store = await module.relationalDb.createNamespace<${v.pascalCaseName}>(
|
|
4281
|
+
namespace,
|
|
4282
|
+
);
|
|
4283
|
+
|
|
4284
|
+
// Create a filter for the processor
|
|
4285
|
+
const filter: ProcessorFilter = {
|
|
4286
|
+
branch: ["main"],
|
|
4287
|
+
documentId: ["*"],
|
|
4288
|
+
documentType: [${getDocumentType(v.documentTypes)}],
|
|
4289
|
+
scope: ["global"],
|
|
4290
|
+
};
|
|
4291
|
+
|
|
4292
|
+
// Create the processor
|
|
4293
|
+
const processor = new ${v.pascalCaseName}(namespace, filter, store);
|
|
4294
|
+
return [
|
|
4295
|
+
{
|
|
4296
|
+
processor,
|
|
4297
|
+
filter,
|
|
4298
|
+
},
|
|
4299
|
+
];
|
|
4300
|
+
}
|
|
4301
|
+
`.raw;
|
|
4302
|
+
//#endregion
|
|
4303
|
+
//#region src/templates/processors/relational-db/index.ts
|
|
4304
|
+
const relationalDbIndexTemplate = ts`
|
|
4305
|
+
export * from "./factory.js";
|
|
4306
|
+
export * from "./processor.js";
|
|
4307
|
+
`.raw;
|
|
4308
|
+
//#endregion
|
|
4309
|
+
//#region src/templates/processors/relational-db/migrations.ts
|
|
4310
|
+
const relationalDbMigrationsTemplate = () => ts`
|
|
4311
|
+
import type { IRelationalDb } from "@powerhousedao/reactor-browser"
|
|
4312
|
+
|
|
4313
|
+
export async function up(db: IRelationalDb<any>): Promise<void> {
|
|
4314
|
+
// Create table
|
|
4315
|
+
await db.schema
|
|
4316
|
+
.createTable("todo")
|
|
4317
|
+
.addColumn("task", "varchar(255)")
|
|
4318
|
+
.addColumn("status", "boolean")
|
|
4319
|
+
.addPrimaryKeyConstraint("todo_pkey", [
|
|
4320
|
+
"task"
|
|
4321
|
+
])
|
|
4322
|
+
.ifNotExists()
|
|
4323
|
+
.execute();
|
|
4324
|
+
}
|
|
4325
|
+
|
|
4326
|
+
export async function down(db: IRelationalDb<any>): Promise<void> {
|
|
4327
|
+
// drop table
|
|
4328
|
+
await db.schema.dropTable("todo").execute();
|
|
4329
|
+
}
|
|
4330
|
+
`.raw;
|
|
4331
|
+
//#endregion
|
|
4332
|
+
//#region src/templates/processors/relational-db/processor.ts
|
|
4333
|
+
const defaultNamespaceComment = "// Default namespace: `${this.name}_${driveId.replaceAll(\"-\", \"_\")}`";
|
|
4334
|
+
const relationalDbProcessorTemplate = (v) => ts`
|
|
4335
|
+
import { RelationalDbProcessor } from "@powerhousedao/reactor-browser";
|
|
4336
|
+
import type { OperationWithContext } from "document-model";
|
|
4337
|
+
import { up } from "./migrations.js";
|
|
4338
|
+
import type { DB } from "./schema.js";
|
|
4339
|
+
|
|
4340
|
+
export class ${v.pascalCaseName} extends RelationalDbProcessor<DB> {
|
|
4341
|
+
onOperations(operations: OperationWithContext[]): Promise<void> {
|
|
4342
|
+
return Promise.resolve();
|
|
4343
|
+
}
|
|
4344
|
+
|
|
4345
|
+
onDisconnect(): Promise<void> {
|
|
4346
|
+
return Promise.resolve();
|
|
4347
|
+
}
|
|
4348
|
+
|
|
4349
|
+
static override getNamespace(driveId: string): string {
|
|
4350
|
+
${defaultNamespaceComment}
|
|
4351
|
+
return super.getNamespace(driveId);
|
|
4352
|
+
}
|
|
4353
|
+
|
|
4354
|
+
override async initAndUpgrade(): Promise<void> {
|
|
4355
|
+
await up(this.relationalDb);
|
|
4356
|
+
}
|
|
4357
|
+
}
|
|
4358
|
+
`.raw;
|
|
4359
|
+
//#endregion
|
|
4360
|
+
//#region src/templates/processors/relational-db/schema.ts
|
|
4361
|
+
const relationalDbSchemaTemplate = () => ts`
|
|
4362
|
+
export interface Todo {
|
|
4363
|
+
status: boolean | null;
|
|
4364
|
+
task: string;
|
|
4365
|
+
}
|
|
4366
|
+
|
|
4367
|
+
export interface DB {
|
|
4368
|
+
todo: Todo;
|
|
4369
|
+
}
|
|
4370
|
+
`.raw;
|
|
4371
|
+
//#endregion
|
|
4372
|
+
//#region src/templates/subgraphs/index-file.ts
|
|
4373
|
+
const subgraphIndexFileTemplate = (v) => ts`
|
|
4374
|
+
import { BaseSubgraph } from "@powerhousedao/reactor-api";
|
|
4375
|
+
import type { DocumentNode } from "graphql";
|
|
4376
|
+
import { schema } from "./schema.js";
|
|
4377
|
+
import { getResolvers } from "./resolvers.js";
|
|
4378
|
+
|
|
4379
|
+
export class ${v.pascalCaseName}Subgraph extends BaseSubgraph {
|
|
4380
|
+
name = "${v.kebabCaseName}";
|
|
4381
|
+
typeDefs: DocumentNode = schema;
|
|
4382
|
+
resolvers = getResolvers(this);
|
|
4383
|
+
additionalContextFields = {};
|
|
4384
|
+
async onSetup() {}
|
|
4385
|
+
async onDisconnect() {}
|
|
4386
|
+
}
|
|
4387
|
+
`.raw;
|
|
4388
|
+
//#endregion
|
|
4389
|
+
//#region src/templates/subgraphs/lib-file.ts
|
|
4390
|
+
const subgraphLibFileTemplate = () => ts`
|
|
4391
|
+
/**
|
|
4392
|
+
* This is a scaffold file meant for customization.
|
|
4393
|
+
* Delete the file and run the code generator again to have it reset
|
|
4394
|
+
*/
|
|
4395
|
+
`.raw;
|
|
4396
|
+
//#endregion
|
|
4397
|
+
//#region src/templates/subgraphs/custom-schema.ts
|
|
4398
|
+
const customSubgraphSchemaTemplate = (v) => ts`
|
|
4399
|
+
import { gql } from "graphql-tag";
|
|
4400
|
+
import type { DocumentNode } from "graphql";
|
|
4401
|
+
|
|
4402
|
+
export const schema: DocumentNode = gql\`
|
|
4403
|
+
"""
|
|
4404
|
+
${v.pascalCaseName} Queries
|
|
4405
|
+
"""
|
|
4406
|
+
type ${v.pascalCaseName}Queries {
|
|
4407
|
+
example(driveId: String!): String
|
|
4408
|
+
}
|
|
4409
|
+
|
|
4410
|
+
type Query {
|
|
4411
|
+
${v.camelCaseName}: ${v.pascalCaseName}Queries!
|
|
4412
|
+
}
|
|
4413
|
+
|
|
4414
|
+
\`
|
|
4415
|
+
`.raw;
|
|
4416
|
+
//#endregion
|
|
4417
|
+
//#region src/templates/subgraphs/custom-resolvers.ts
|
|
4418
|
+
const customSubgraphResolversTemplate = (v) => ts`
|
|
4419
|
+
import { type ISubgraph } from "@powerhousedao/reactor-api";
|
|
4420
|
+
|
|
4421
|
+
export const getResolvers = (subgraph: ISubgraph): Record<string, unknown> => {
|
|
4422
|
+
const reactor = subgraph.reactorClient;
|
|
4423
|
+
|
|
4424
|
+
return ({
|
|
4425
|
+
Query: {
|
|
4426
|
+
${v.camelCaseName}: () => ({}), // namespace resolver for nested queries
|
|
4427
|
+
},
|
|
4428
|
+
${v.pascalCaseName}Queries: {
|
|
4429
|
+
example: async (parent: unknown, args: { driveId: string }) => {
|
|
4430
|
+
return "example";
|
|
4431
|
+
},
|
|
4432
|
+
},
|
|
4433
|
+
});
|
|
4434
|
+
};
|
|
4435
|
+
`.raw;
|
|
4436
|
+
//#endregion
|
|
4437
|
+
//#region src/templates/subgraphs/document-model-schema.ts
|
|
4438
|
+
const documentModelSubgraphSchemaTemplate = (v) => {
|
|
4439
|
+
const mutationFields = v.modules.flatMap((module) => module.operations.map((op) => ` ${v.pascalCaseDocumentType}_${camel$1(op.name)}(driveId:String, docId:PHID, input:${v.pascalCaseDocumentType}_${pascal$1(op.name)}Input): Int`)).join("\n");
|
|
4440
|
+
const moduleSchemas = v.modules.map((module) => {
|
|
4441
|
+
return `${`\n"""\nModule: ${pascal$1(module.name)}\n"""`}\n${module.operations.map((op) => op.schema).join("\n")}`;
|
|
4442
|
+
}).join("\n");
|
|
4443
|
+
return `import { gql } from "graphql-tag";
|
|
4444
|
+
import type { DocumentNode } from "graphql";
|
|
4445
|
+
|
|
4446
|
+
export const schema: DocumentNode = gql\`
|
|
4447
|
+
"""
|
|
4448
|
+
Queries: ${v.pascalCaseDocumentType} Document
|
|
4449
|
+
"""
|
|
4450
|
+
|
|
4451
|
+
type ${v.pascalCaseDocumentType}Queries {
|
|
4452
|
+
getDocument(docId: PHID!, driveId: PHID): ${v.pascalCaseDocumentType}
|
|
4453
|
+
getDocuments(driveId: String): [${v.pascalCaseDocumentType}!]
|
|
4454
|
+
}
|
|
4455
|
+
|
|
4456
|
+
type Query {
|
|
4457
|
+
${v.pascalCaseDocumentType}: ${v.pascalCaseDocumentType}Queries
|
|
4458
|
+
}
|
|
4459
|
+
|
|
4460
|
+
"""
|
|
4461
|
+
Mutations: ${v.pascalCaseDocumentType}
|
|
4462
|
+
"""
|
|
4463
|
+
type Mutation {
|
|
4464
|
+
|
|
4465
|
+
${v.pascalCaseDocumentType}_createDocument(name:String!, driveId:String): String
|
|
4466
|
+
|
|
4467
|
+
${mutationFields}
|
|
4468
|
+
}
|
|
4469
|
+
|
|
4470
|
+
${moduleSchemas}
|
|
4471
|
+
\`
|
|
4472
|
+
`;
|
|
4473
|
+
};
|
|
4474
|
+
function pascal$1(name) {
|
|
4475
|
+
return name.split(/[-_\s]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
|
|
4476
|
+
}
|
|
4477
|
+
function camel$1(name) {
|
|
4478
|
+
const p = pascal$1(name);
|
|
4479
|
+
return p.charAt(0).toLowerCase() + p.slice(1);
|
|
4480
|
+
}
|
|
4481
|
+
//#endregion
|
|
4482
|
+
//#region src/templates/subgraphs/document-model-resolvers.ts
|
|
4483
|
+
const documentModelSubgraphResolversTemplate = (v) => {
|
|
4484
|
+
const inputTypeImports = v.modules.flatMap((m) => m.operations.map((o) => `${pascal(o.name)}Input`)).join(",\n ");
|
|
4485
|
+
const operationMutations = v.modules.flatMap((module) => module.operations.map((op) => ` ${v.pascalCaseDocumentType}_${camel(op.name)}: async (_: unknown, args: { docId: string, input: ${pascal(op.name)}Input}) => {
|
|
4486
|
+
const { docId, input } = args;
|
|
4487
|
+
const doc = await reactorClient.get<${v.pascalCaseDocumentType}Document>(docId);
|
|
4488
|
+
if(!doc) {
|
|
4489
|
+
throw new Error("Document not found");
|
|
4490
|
+
}
|
|
4491
|
+
|
|
4492
|
+
await reactorClient.execute(
|
|
4493
|
+
docId,
|
|
4494
|
+
"main",
|
|
4495
|
+
[actions.${camel(op.name)}(input)]
|
|
4496
|
+
);
|
|
4497
|
+
|
|
4498
|
+
return true;
|
|
4499
|
+
},
|
|
4500
|
+
`)).join("\n");
|
|
4501
|
+
return `import type { BaseSubgraph } from "@powerhousedao/reactor-api";
|
|
4502
|
+
import { addFile } from "@powerhousedao/shared/document-drive";
|
|
4503
|
+
import { setName } from "document-model";
|
|
4504
|
+
import {
|
|
4505
|
+
actions,
|
|
4506
|
+
${v.documentTypeVariableName},
|
|
4507
|
+
} from "${v.documentModelDir}";
|
|
4508
|
+
import type {
|
|
4509
|
+
${v.phDocumentTypeName},
|
|
4510
|
+
${inputTypeImports}
|
|
4511
|
+
} from "${v.documentModelDir}";
|
|
4512
|
+
|
|
4513
|
+
export const getResolvers = (subgraph: BaseSubgraph): Record<string, unknown> => {
|
|
4514
|
+
const reactorClient = subgraph.reactorClient;
|
|
4515
|
+
|
|
4516
|
+
return ({
|
|
4517
|
+
Query: {
|
|
4518
|
+
${v.pascalCaseDocumentType}: async () => {
|
|
4519
|
+
return {
|
|
4520
|
+
getDocument: async (args: { docId: string, driveId: string }) => {
|
|
4521
|
+
const { docId, driveId } = args;
|
|
4522
|
+
|
|
4523
|
+
if(!docId) {
|
|
4524
|
+
throw new Error("Document id is required");
|
|
4525
|
+
}
|
|
4526
|
+
|
|
4527
|
+
if(driveId) {
|
|
4528
|
+
const children = await reactorClient.getChildren(driveId);
|
|
4529
|
+
const childIds = children.results.map(d => d.header.id);
|
|
4530
|
+
if(!childIds.includes(docId)) {
|
|
4531
|
+
throw new Error(\`Document with id \${docId} is not part of \${driveId}\`);
|
|
4532
|
+
}
|
|
4533
|
+
}
|
|
4534
|
+
|
|
4535
|
+
const doc = await reactorClient.get<${v.phDocumentTypeName}>(docId);
|
|
4536
|
+
return {
|
|
4537
|
+
driveId: driveId,
|
|
4538
|
+
...doc,
|
|
4539
|
+
...doc.header,
|
|
4540
|
+
created: doc.header.createdAtUtcIso,
|
|
4541
|
+
lastModified: doc.header.lastModifiedAtUtcIso,
|
|
4542
|
+
state: doc.state.global,
|
|
4543
|
+
stateJSON: doc.state.global,
|
|
4544
|
+
revision: doc.header?.revision?.global ?? 0,
|
|
4545
|
+
};
|
|
4546
|
+
},
|
|
4547
|
+
getDocuments: async (args: { driveId?: string }) => {
|
|
4548
|
+
const { driveId } = args;
|
|
4549
|
+
const result = await reactorClient.find({ type: ${v.documentTypeVariableName}, ...(driveId ? { parentId: driveId } : {}) });
|
|
4550
|
+
return result.results.map((_doc) => {
|
|
4551
|
+
const doc = _doc as ${v.phDocumentTypeName};
|
|
4552
|
+
return {
|
|
4553
|
+
driveId: driveId,
|
|
4554
|
+
...doc,
|
|
4555
|
+
...doc.header,
|
|
4556
|
+
created: doc.header.createdAtUtcIso,
|
|
4557
|
+
lastModified: doc.header.lastModifiedAtUtcIso,
|
|
4558
|
+
state: doc.state.global,
|
|
4559
|
+
stateJSON: doc.state.global,
|
|
4560
|
+
revision: doc.header?.revision?.global ?? 0,
|
|
4561
|
+
};
|
|
4562
|
+
});
|
|
4563
|
+
},
|
|
4564
|
+
};
|
|
4565
|
+
},
|
|
4566
|
+
},
|
|
4567
|
+
Mutation: {
|
|
4568
|
+
${v.pascalCaseDocumentType}_createDocument: async (_: unknown, args: { name: string, driveId?: string }) => {
|
|
4569
|
+
const { driveId, name } = args;
|
|
4570
|
+
const document = await reactorClient.createEmpty(${v.documentTypeVariableName});
|
|
4571
|
+
|
|
4572
|
+
if(driveId) {
|
|
4573
|
+
await reactorClient.execute(
|
|
4574
|
+
driveId,
|
|
4575
|
+
"main",
|
|
4576
|
+
[addFile({
|
|
4577
|
+
name,
|
|
4578
|
+
id: document.header.id,
|
|
4579
|
+
documentType: ${v.documentTypeVariableName},
|
|
4580
|
+
})],
|
|
4581
|
+
);
|
|
4582
|
+
}
|
|
4583
|
+
|
|
4584
|
+
if(name) {
|
|
4585
|
+
await reactorClient.execute(
|
|
4586
|
+
document.header.id,
|
|
4587
|
+
"main",
|
|
4588
|
+
[setName(name)],
|
|
4589
|
+
);
|
|
4590
|
+
}
|
|
4591
|
+
|
|
4592
|
+
return document.header.id;
|
|
4593
|
+
},
|
|
4594
|
+
|
|
4595
|
+
${operationMutations}
|
|
4596
|
+
},
|
|
4597
|
+
});
|
|
4598
|
+
};
|
|
4599
|
+
`;
|
|
4600
|
+
};
|
|
4601
|
+
function pascal(name) {
|
|
4602
|
+
return name.split(/[-_\s]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
|
|
4603
|
+
}
|
|
4604
|
+
function camel(name) {
|
|
4605
|
+
const p = pascal(name);
|
|
4606
|
+
return p.charAt(0).toLowerCase() + p.slice(1);
|
|
4607
|
+
}
|
|
4608
|
+
//#endregion
|
|
4609
|
+
export { tsConfigTemplate as $, documentModelHooksFileTemplate as A, claudeSettingsLocalTemplate as At, getModuleExportType as B, appDriveContentsFileTemplate as Bt, makeActionsImports as C, documentModelsIndexTemplate as Ct, documentModelSrcIndexFileTemplate as D, dockerfileTemplate as Dt, documentModelSrcUtilsTemplate as E, nginxConfTemplate as Et, documentModelPhFactoriesFileTemplate as F, folderTreeFileTemplate as Ft, documentModelGenControllerFileTemplate as G, documentModelDocumentTypeTemplate as H, documentModelOperationsModuleOperationsFileTemplate as I, appFoldersFileTemplate as It, documentEditorModuleFileTemplate as J, documentModelGenActionsFileTemplate as K, documentModelOperationsModuleErrorFileTemplate as L, appFilesFileTemplate as Lt, documentModelGenTypesTemplate as M, appEditorFileTemplate as Mt, documentModelSchemaIndexTemplate as N, appConfigFileTemplate as Nt, documentModelModuleFileTemplate as O, connectEntrypointTemplate as Ot, documentModelGenReducerFileTemplate as P, driveExplorerNavigationBreadcrumbsFileTemplate as Pt, viteConfigTemplate as Q, documentModelOperationsModuleCreatorsFileTemplate as R, emptyStateFileTemplate as Rt, makeActionImportNames as S, editorsTemplate as St, documentModelTestFileTemplate as T, switchboardEntrypointTemplate as Tt, documentModelDocumentSchemaFileTemplate as U, documentModelGenIndexFileTemplate as V, createDocumentFileTemplate as Vt, documentModelGenCreatorsFileTemplate as W, docsFromCliHelpTemplate as X, documentEditorEditorFileTemplate as Y, vitestConfigTemplate as Z, analyticsFactoryTemplate as _, gitIgnoreTemplate as _t, subgraphLibFileTemplate as a, buildPowerhouseConfigTemplate as at, upgradeManifestTemplate as b, eslintConfigTemplate as bt, relationalDbProcessorTemplate as c, exportsTemplate as ct, relationalDbFactoryTemplate as d, mcpTemplate as dt, tsconfigPathsTemplate as et, processorsIndexTemplate as f, mainTsxTemplate as ft, analyticsIndexTemplate as g, indexHtmlTemplate as gt, analyticsProcessorTemplate as h, legacyIndexHtmlTemplate as ht, customSubgraphSchemaTemplate as i, powerhouseManifestTemplate as it, documentModelGenUtilsTemplate as j, agentsTemplate as jt, documentModelIndexTemplate as k, cursorMcpTemplate as kt, relationalDbMigrationsTemplate as l, packageJsonTemplate as lt, factoryBuildersTemplate as m, indexTsTemplate as mt, documentModelSubgraphSchemaTemplate as n, styleTemplate as nt, subgraphIndexFileTemplate as o, packageJsonExportsTemplate as ot, processorsFactoryTemplate as p, licenseTemplate as pt, documentModelRootActionsFileTemplate as q, customSubgraphResolversTemplate as r, readmeTemplate as rt, relationalDbSchemaTemplate as s, packageJsonScriptsTemplate as st, documentModelSubgraphResolversTemplate as t, subgraphsIndexTemplate as tt, relationalDbIndexTemplate as u, npmrcTemplate as ut, documentModelUtilsTemplate as v, syncAndPublishWorkflowTemplate as vt, makeTestCaseForAction as w, documentModelsTemplate as wt, documentModelOperationsModuleTestFileTemplate as x, editorsIndexTemplate as xt, upgradeTransitionTemplate as y, geminiSettingsTemplate as yt, documentModelOperationModuleActionsFileTemplate as z, driveExplorerFileTemplate as zt };
|
|
4610
|
+
|
|
4611
|
+
//# sourceMappingURL=templates-DBvz_qPL.mjs.map
|