API stores form instruments that import React, which the web app and gateway then refuse to render
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 74/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- react, typescript
Research direction
Start in apps/api/src/instruments/instruments.service.ts:94-109 and compare its bundle validation with the React rule in packages/instrument-interpreter/src/index.ts:30-37; check how a shared package can expose that rule to the API. Run the named service test in apps/api/src/instruments/__tests__/instruments.service.test.ts and the repository-import e2e test in testing/src/specs/admin-instrument-repos.spec.ts. Done when the API rejects non-interactive bundles importing React with a 422, and both tests pass.
Written by the indexing model from the issue text.
Description
The API stores form instruments that every client then refuses to render. InstrumentInterpreter.interpret rejects a non-interactive instrument whose bundle imports /runtime/v1/react@* or /runtime/v1/react-dom@* (findReactImport, added in f46a257eb). That check runs only in the browser. InstrumentsService.create evaluates the same bundle server-side and validates it against $AnyInstrument, but never applies the React rule. So a form whose block imports useState, whether from an instrument repository import or a bundle upload, is accepted, stored and listed. Every attempt to open it in apps/web or the gateway then fails with "Cannot import '/runtime/v1/[email protected]/index.js' in an instrument of kind 'FORM'". The commit message says the browser is "the earliest point the two kinds can be told apart", but the API evaluates the bundle and knows its kind first.
Where
apps/api/src/instruments/instruments.service.ts:94-109:
const result = await this.virtualizationService.eval(bundle);
// ...
const parseResult = await $AnyInstrument.safeParseAsync(result.value);
if (!parseResult.success) {
throw new UnprocessableEntityException({ /* ... */ });
}
const instance = parseResult.data;
compared with the client-side rule, packages/instrument-interpreter/src/index.ts:30-37:
if (instrument.kind !== 'INTERACTIVE') {
const reactImport = findReactImport(bundle);
if (reactImport) {
throw new Error(
`Cannot import '${reactImport}' in an instrument of kind '${instrument.kind}': React is available only to interactive instruments, ...`
Reproduce
- In an instrument repository, add a form under
lib/forms/whose block imports{ useState }from/runtime/v1/[email protected]. - Add the repository in
apps/web(Admin > Instrument Repositories) and assign the form to a group. - Start a session and open the form.
Actual: the import succeeds and the form is listed, but opening it shows the instrument error page with "React is available only to interactive instruments".
Expected: the API rejects the bundle with 422 and the same message, so the repository import reports that instrument as failed and it is never offered to users.
Tests
apps/api/src/instruments/__tests__/instruments.service.test.ts: it('should reject a non-interactive bundle that imports react, so no stored form fails in every client').
e2e, in testing/src/specs/admin-instrument-repos.spec.ts: a repository fixture form that imports react is reported as failed, not imported.
Suggested fix
Move findReactImport (or the whole kind check) into a shared package the API can import, such as @opendatacapture/instrument-utils or runtime-internal. Call it in InstrumentsService.create after the $AnyInstrument parse and throw UnprocessableEntityException with the interpreter's message. Keep the client-side check for bundles that never pass through the API, such as the playground.
- Dominant language
- TypeScript
- Stars
- 119
- Forks
- 19
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 56
Getting set up
- Ships a Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from DouglasNeuroInformatics/OpenDataCapture
-
Area: Playground Bug Difficulty: Low Good First Issue Priority: Low
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
DouglasNeuroInformatics/OpenDataCapture#1805 ·
Maintainers usually reply within 1 day
-
Area: Instruments Bug Difficulty: Low Priority: Low
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
DouglasNeuroInformatics/OpenDataCapture#1801 ·
Maintainers usually reply within 1 day
-
Area: Instruments Bug Difficulty: Low Good First Issue Priority: Low
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
DouglasNeuroInformatics/OpenDataCapture#1800 ·
Maintainers usually reply within 1 day
-
Area: Instruments Bug Difficulty: Low Good First Issue Priority: Low
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
DouglasNeuroInformatics/OpenDataCapture#1799 ·
Maintainers usually reply within 1 day
-
Area: Instruments Bug Difficulty: Low Performance Priority: Medium
Difficulty 2/5 1-3 hours Newbie friendliness 83/100
DouglasNeuroInformatics/OpenDataCapture#1795 ·
Maintainers usually reply within 1 day
All issues in DouglasNeuroInformatics/OpenDataCapture
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
farbenmeer/tapi#531 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
naver/egjs-flicking#971 ·
-
Renderer treats a sub-pixel width difference as a resize, which cancels the `motion()` entranceOpen
Difficulty 1/5 Under an hour Newbie friendliness 85/100
Maintainers usually reply within 1 day
-
Tenant
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
MTES-MCT/Dossier-Facile-Frontend#2061 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
backnotprop/plannotator#1784 ·
Maintainers usually reply within 1 day