mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-03-08 01:53:30 +01:00
Task: STRUCT-004
Branch: cline-dev-dishant
Cross-branch notes: none -- no shared dependencies with Richard phase 9/6 work
- ProjectFormatDetector class (injectable filesystem, fully testable)
- detect() async + detectSync() sync variants
- Detects legacy (project.json) vs v2 (nodegx.project.json + _registry.json)
- Returns { format, confidence, indicators } for transparent decision-making
- Scoring system: v2 needs score >= 2 to avoid false positives
- Errors in exists() treated as not-found (graceful degradation)
- V2_INDICATORS / LEGACY_INDICATORS sentinel constants exported
- createNodeDetector() factory for Node.js/test contexts
- Convenience methods: getFormat(), isV2(), isLegacy()
- 30 tests in tests/io/ProjectFormatDetector.test.ts
- detect(): 12 cases (all combinations, async fs, error handling)
- detectSync(): 4 cases (incl. throws on async fs)
- Convenience methods: 7 cases
- Constants: 3 cases
- createNodeDetector() integration: 4 cases (real tmp dirs)
- Updated tests/io/index.ts to export detector tests
- Updated PROGRESS-dishant.md: STRUCT-001/002/003/004 all marked complete
9.1 KiB
9.1 KiB
Phase 10A — AI-Powered Development: Dishant Progress
Developer: Dishant
Branch: cline-dev-dishant
Last Updated: 2026-02-19
Task Status
| Task ID | Title | Status | Notes |
|---|---|---|---|
| STRUCT-001 | JSON Schema Definition | COMPLETE | 8 schemas + validator + tests (33/33 pass) |
| STRUCT-002 | Export Engine Core | COMPLETE | ProjectExporter + 50 unit tests |
| STRUCT-003 | Import Engine Core | COMPLETE | ProjectImporter + 55 unit tests incl. round-trip validation |
| STRUCT-004 | Editor Format Detection | COMPLETE | ProjectFormatDetector + 30 unit tests incl. integration |
| STRUCT-005 | Lazy Loading | TODO | Unblocked by STRUCT-004 |
| STRUCT-006 | Save Logic | TODO | Unblocked by STRUCT-005 |
| STRUCT-007 | Migration Wizard UI | TODO | Unblocked by STRUCT-006 |
| STRUCT-008 | Testing and Validation | TODO | Unblocked by STRUCT-007 |
| STRUCT-009 | Documentation | TODO | Unblocked by STRUCT-008 |
STRUCT-001 — JSON Schema Definition COMPLETE
Completed: 2026-02-18
What was built
All files under packages/noodl-editor/src/editor/src/schemas/:
| File | Schema ID | Describes |
|---|---|---|
project-v2.schema.json |
https://opennoodl.dev/schemas/project-v2.json |
Root project metadata |
component.schema.json |
https://opennoodl.dev/schemas/component-v2.json |
Component metadata |
nodes.schema.json |
https://opennoodl.dev/schemas/nodes-v2.json |
Node graph definitions |
connections.schema.json |
https://opennoodl.dev/schemas/connections-v2.json |
Connection/wire definitions |
registry.schema.json |
https://opennoodl.dev/schemas/registry-v2.json |
Component index |
routes.schema.json |
https://opennoodl.dev/schemas/routes-v2.json |
URL route definitions |
styles.schema.json |
https://opennoodl.dev/schemas/styles-v2.json |
Global styles + variants |
model.schema.json |
https://opennoodl.dev/schemas/model-v2.json |
Backend data model definitions |
validator.ts— SchemaValidator singleton, per-schema convenience methods, Ajv v8 + ajv-formatsindex.ts— re-exports all schemas, validator, TypeScript interfacestests/schemas/schema-validator.test.ts— 33 test cases, all passing
Key decisions
additionalProperties: trueon nodes/connections — open-ended by design- Port type is
oneOf [string, object]— Noodl uses both formats strict: falseon Ajv — schemas usedescriptionindefinitionsrequire()forajv-formats— avoids TS type conflict
STRUCT-002 — Export Engine Core COMPLETE
Completed: 2026-02-19
What was built
| File | Purpose |
|---|---|
packages/noodl-editor/src/editor/src/io/ProjectExporter.ts |
Core exporter class + legacy types + helpers |
packages/noodl-editor/tests/io/ProjectExporter.test.ts |
50 unit tests |
packages/noodl-editor/tests/io/index.ts |
Test barrel export |
Architecture
ProjectExporter is pure — no filesystem access. Takes LegacyProject, returns ExportResult { files[], stats }.
Output layout:
nodegx.project.json
nodegx.routes.json (conditional)
nodegx.styles.json (conditional)
components/
_registry.json
Header/
component.json
nodes.json
connections.json
Key decisions
- Pure/filesystem-agnostic — caller writes files
- Node tree flattening — recursive tree to flat array with parent/children IDs
- Styles/routes files conditional — only produced when content exists
- Legacy
stateParamaterstypo normalised tostateParameters - Metadata split — styles/routes extracted, rest stays in project file
- Original legacy path preserved in
component.jsonfor round-trip fidelity - Empty
parameters: {}omitted from output
STRUCT-003 — Import Engine Core COMPLETE
Completed: 2026-02-19
What was built
| File | Purpose |
|---|---|
packages/noodl-editor/src/editor/src/io/ProjectImporter.ts |
Core importer class + helpers |
packages/noodl-editor/tests/io/ProjectImporter.test.ts |
55 unit tests incl. round-trip |
Architecture
ProjectImporter is pure — no filesystem access. Takes ImportInput (all file contents), returns ImportResult { project, warnings }.
ImportInput {
project: ProjectV2File (nodegx.project.json)
registry: RegistryV2File (components/_registry.json)
routes?: RoutesV2File (nodegx.routes.json)
styles?: StylesV2File (nodegx.styles.json)
components: Record<path, { component, nodes, connections }>
}
Key functions exported
| Function | Purpose |
|---|---|
unflattenNodes(flat) |
Reconstructs recursive legacy node tree from flat NodeV2 array |
toLegacyName(componentFile, registryPath) |
Converts v2 path back to legacy name (uses preserved path field) |
Key decisions
- Pure/filesystem-agnostic — symmetric with ProjectExporter
unflattenNodes— two-pass: build map, then wire children in ordertoLegacyName— preferscomponent.path(preserved original) for perfect round-trip; falls back to reconstructionstateParameters→stateParamatersreversal — restores legacy typo- Metadata merge — routes/styles merged back into
project.metadata - Non-fatal warnings — component failures collected, not thrown
- Round-trip validated — 20 round-trip tests covering all data types
Test coverage (55 tests)
unflattenNodes— 11 cases (empty, single, parent-child, deep nesting, order, field restoration)toLegacyName— 4 cases (path field, fallback, root, cloud)ProjectImporter.import()— 20 cases (basic, metadata, variants, components, nodes)- Round-trip (export → import) — 20 cases (all data types, edge cases)
STRUCT-004 — Editor Format Detection COMPLETE
Completed: 2026-02-19
What was built
| File | Purpose |
|---|---|
packages/noodl-editor/src/editor/src/io/ProjectFormatDetector.ts |
Format detection utility |
packages/noodl-editor/tests/io/ProjectFormatDetector.test.ts |
30 unit + integration tests |
Architecture
ProjectFormatDetector accepts a DetectorFilesystem interface (injectable, testable). Supports both async detect() and sync detectSync().
Detection logic (priority order):
nodegx.project.json+components/_registry.json→ v2 / high confidence- Either v2 indicator alone → v2 / medium confidence
project.jsononly → legacy / high confidence- Nothing found → unknown / low confidence
- Both legacy + v2 → v2 wins (migration in progress)
Key exports
| Export | Purpose |
|---|---|
ProjectFormatDetector |
Main class |
DetectorFilesystem |
Interface for filesystem injection |
V2_INDICATORS |
Sentinel filenames for v2 format |
LEGACY_INDICATORS |
Sentinel filenames for legacy format |
createNodeDetector() |
Factory using Node.js fs.existsSync |
ProjectFormat |
`'legacy' |
FormatDetectionResult |
{ format, confidence, indicators } |
Key decisions
- Injectable filesystem — no platform singleton dependency, fully testable
- Supports both sync and async
exists()—detectSync()throws if async fs injected createNodeDetector()factory for Node.js/test contexts- Scoring system — v2 needs score ≥ 2 (nodegx.project.json=2, registry=2, components dir=1)
- Errors in
exists()treated as "not found" — graceful degradation
Test coverage (30 tests)
detect()— 12 cases (all format combinations, async fs, error handling)detectSync()— 4 cases (v2, legacy, unknown, throws on async fs)- Convenience methods — 7 cases (getFormat, isV2, isLegacy)
- Constants — 3 cases
createNodeDetector()integration — 4 cases (real filesystem with tmp dirs)
Decisions and Learnings
- [2026-02-18] Ajv v8 + ajv-formats: use
require()for ajv-formats to avoid TS type conflict with root-level ajv-formats package - [2026-02-19] Legacy
stateParamaterstypo (missing 'e') is real — must be preserved in round-trip. Exporter normalises tostateParameters, importer reverses it. - [2026-02-19]
component.pathfield is the key to perfect round-trip fidelity — always preserve the original legacy name in the v2 component file - [2026-02-19]
unflattenNodesneeds two passes — first build the node map, then wire children in order using the children ID array - [2026-02-19] Format detection: inject filesystem interface rather than using platform singleton — makes the utility testable without Electron context and usable in Node.js scripts/tests