on master help sign in
Check static and direct data-page mains
1e17d5fiko
README.md+2
@@ -44,6 +44,8 @@ tools/hoon --raw --cord src=tests/golden/foo/source.jibb 'tree:(golden /a/b/jibb
44
44
45`tools/testdata/selftest` tests both tools against a stub library, and `tools/doc-blocks` against Markdown it writes. `tools/importer-test` compares the partial Hoon importer's fixture projections against their golden file. `tools/type-import-test`, `tools/type-import-overlap-test`, `tools/type-import-structural-test`, `tools/type-union-source-test`, `tools/type-union-alias-overlap-test`, `tools/type-full-overlap-test`, `tools/type-generic-recursion-test`, `tools/type-generic-construction-test`, `tools/type-generic-pattern-test`, `tools/type-deferred-test`, `tools/type-deferred-prepared-test`, `tools/type-deferred-resolver-test`, `tools/type-deferred-context-test`, `tools/type-empty-test`, `tools/type-cell-branch-test`, `tools/type-tlon-test`, `tools/type-mark-test`, `tools/type-poke-test` and `tools/type-api-paths-test` check importer/checker, prelude and partial API path analysis boundaries under eval; the ship suite runs the same fixtures.
45`tools/testdata/selftest` tests both tools against a stub library, and `tools/doc-blocks` against Markdown it writes. `tools/importer-test` compares the partial Hoon importer's fixture projections against their golden file. `tools/type-import-test`, `tools/type-import-overlap-test`, `tools/type-import-structural-test`, `tools/type-union-source-test`, `tools/type-union-alias-overlap-test`, `tools/type-full-overlap-test`, `tools/type-generic-recursion-test`, `tools/type-generic-construction-test`, `tools/type-generic-pattern-test`, `tools/type-deferred-test`, `tools/type-deferred-prepared-test`, `tools/type-deferred-resolver-test`, `tools/type-deferred-context-test`, `tools/type-empty-test`, `tools/type-cell-branch-test`, `tools/type-tlon-test`, `tools/type-mark-test`, `tools/type-poke-test` and `tools/type-api-paths-test` check importer/checker, prelude and partial API path analysis boundaries under eval; the ship suite runs the same fixtures.
46
46
47+The focused `tools/type-page-main-test` fully checks the byte-identical static about page and direct blog/todo data pages through the real readers, with exact main types, downstream imports and fail-closed syntax/evidence controls; the same fixture runs on the fake ship. It does not render pages or execute requests.
48+
47The focused `tools/type-program-main-test` checks the narrow closed, subscription-free `main` slice and its shared fake-ship fixture. `tools/type-program-init-test` checks transactional initializer recovery for a synthetic direct program, including exact exported and imported types and rejection boundaries. `tools/type-program-update-test` checks derived result context for an unannotated two-slot update, including a union/boolean branch, downstream types and fail-closed controls under eval with a shared fake-ship arm. `tools/type-program-watch-test` uses the real todo readers with synthetic program components to check one direct request value against merged API-source evidence and reject hand-built, indirect, parameterized, diff and ambiguous subscriptions. These are static checks only: they do not run an island or establish runtime watching. `tools/type-todo-full-test` additionally checks the byte-identical todo island through both full-module APIs, verifies its exact closed `main` type and structural downstream import, and runs rejection controls.
49The focused `tools/type-program-main-test` checks the narrow closed, subscription-free `main` slice and its shared fake-ship fixture. `tools/type-program-init-test` checks transactional initializer recovery for a synthetic direct program, including exact exported and imported types and rejection boundaries. `tools/type-program-update-test` checks derived result context for an unannotated two-slot update, including a union/boolean branch, downstream types and fail-closed controls under eval with a shared fake-ship arm. `tools/type-program-watch-test` uses the real todo readers with synthetic program components to check one direct request value against merged API-source evidence and reject hand-built, indirect, parameterized, diff and ambiguous subscriptions. These are static checks only: they do not run an island or establish runtime watching. `tools/type-todo-full-test` additionally checks the byte-identical todo island through both full-module APIs, verifies its exact closed `main` type and structural downstream import, and runs rejection controls.
48
50
49### The doc's examples
51### The doc's examples
docs/README.md+2
@@ -2,6 +2,8 @@
2
2
3Jibb's spec is a design doc by @iko, [Jibb — an Elm-architecture UI language for Urbit](https://claude.ai/artifact/DMBr48qzi5z8hcwn2ccPBv), and its repository copy in `spec/`. The repository copy records decisions made on 2026-10-30, including dropping `$?`, adding `%$` as a term literal and pattern, making `@` an unrestricted atom, treating `?` as two atoms for union disjointness, and the milestone 2 type and prelude decisions; the Claude document still needs to be synchronized. This folder also holds where the build stands, what is still open, and how to work on the code.
3Jibb's spec is a design doc by @iko, [Jibb — an Elm-architecture UI language for Urbit](https://claude.ai/artifact/DMBr48qzi5z8hcwn2ccPBv), and its repository copy in `spec/`. The repository copy records decisions made on 2026-10-30, including dropping `$?`, adding `%$` as a term literal and pattern, making `@` an unrestricted atom, treating `?` as two atoms for union disjointness, and the milestone 2 type and prelude decisions; the Claude document still needs to be synchronized. This folder also holds where the build stands, what is still open, and how to work on the code.
4
4
5+The current full-module checker also has a narrow static/data-page slice, documented in [compiler.md](compiler.md) and [status.md](status.md). Its shared eval/ship fixture checks the real about, blog index and todo index sources without claiming rendering or request execution.
6+
5| File | What it holds |
7| File | What it holds |
6| --- | --- |
8| --- | --- |
7| [spec/](spec/) | The doc's tabs, as Markdown |
9| [spec/](spec/) | The doc's tabs, as Markdown |
docs/compiler.md+2
@@ -130,6 +130,8 @@ A two-item tuple with an explicit term, null or loobean head can receive payload
130
130
131A full ordinary module may export a narrow checked program `main` only as a direct, unqualified `(program init update view subs)` call with four simple wing arguments, modulo transparent grouping. The canonical prepared prelude must supply both the `program` value and mold. The ordinary source-backed program fit and all normal mold, value and request-graph checks complete first. The inferred `main` must be closed and invariantly match an instantiated `program` mold from that prelude. The initializer must have a closed gate scheme; its data and model contain no open variables and share the existing 256-node gate-free budget, with `%yes` required for both. The named subscription gate may syntactically return null through transparent casts, groups and definition-free bodies. Alternatively, after a successful request-graph merge, it may return one singleton list containing an unfaced `[%watch request]` pair whose request is a direct `case.face` call with one direct constructor callback. The face must resolve to an imported interface whose member has the exact plain derived-request scheme. Exactly one merged request evidence must match its API path and case, agree with the declaration's inner name, and appear in that interface's own graph; the interface source must equal the evidence's API source. The declaration must have no scry, diffs or path parameters. Pure or hand-built requests, helpers, aliases, re-exports, typed paths, folds, multiple subscriptions and missing or ambiguous evidence fail closed. Arbitrary or aliased mains, API/store kinds, and gates or unknowns in data/model also fail closed. This retains source and graph evidence and permits ordinary downstream imports; it neither executes the program nor implements runtime watching.
131A full ordinary module may export a narrow checked program `main` only as a direct, unqualified `(program init update view subs)` call with four simple wing arguments, modulo transparent grouping. The canonical prepared prelude must supply both the `program` value and mold. The ordinary source-backed program fit and all normal mold, value and request-graph checks complete first. The inferred `main` must be closed and invariantly match an instantiated `program` mold from that prelude. The initializer must have a closed gate scheme; its data and model contain no open variables and share the existing 256-node gate-free budget, with `%yes` required for both. The named subscription gate may syntactically return null through transparent casts, groups and definition-free bodies. Alternatively, after a successful request-graph merge, it may return one singleton list containing an unfaced `[%watch request]` pair whose request is a direct `case.face` call with one direct constructor callback. The face must resolve to an imported interface whose member has the exact plain derived-request scheme. Exactly one merged request evidence must match its API path and case, agree with the declaration's inner name, and appear in that interface's own graph; the interface source must equal the evidence's API source. The declaration must have no scry, diffs or path parameters. Pure or hand-built requests, helpers, aliases, re-exports, typed paths, folds, multiple subscriptions and missing or ambiguous evidence fail closed. Arbitrary or aliased mains, API/store kinds, and gates or unknowns in data/model also fail closed. This retains source and graph evidence and permits ordinary downstream imports; it neither executes the program nor implements runtime watching.
132
132
133+The same post-inference, post-request-graph kind boundary accepts a static `main` by instantiating its ordinary scheme, fitting it to canonical `(element !!)`, requiring the specialized result closed, and publishing that closed canonical scheme. Thus `main view` works when `view` is generic only in its element message. A data page must instead be a direct `(page load view)` call with simple wing arguments and a closed main type invariant against the instantiated canonical `page` mold. Its named load must be a direct gate whose transparent body is exactly `case.face reply`, with a simple wing or direct constructor reply. Page loads and direct program watches share the exact plain derived-member/request-evidence check: unique evidence in the selected interface's own graph, matching API source and inner case name, no typed path segments, diffs or scry. Page aliases, indirect or hand-built/pure/unknown requests, other main types and store paths fail closed. These are interface checks only, not rendering or runtime request claims.
134+
133When only that program's initializer and `main` are unresolved, a bounded transactional retry can derive the initializer's expected gate from the resolved update, view and subscription components. It instantiates all schemes above the greatest variable ID in the module and prelude, reuses the source-backed positional fit and stable replay, and leaves only the initializer's data variable open. The named initializer must be a direct gate whose transparent body is a tuple ending in null. Its own sample fixes data; ordinary inference, product and whole-gate fits, pending-tag and finalization checks, and a fresh ordinary `main` inference must all succeed before the ordinary kind and graph checks publish anything. Aliases, non-main libraries, simultaneous unresolved subscriptions, nonempty subscriptions and unrelated pending tags decline.
135When only that program's initializer and `main` are unresolved, a bounded transactional retry can derive the initializer's expected gate from the resolved update, view and subscription components. It instantiates all schemes above the greatest variable ID in the module and prelude, reuses the source-backed positional fit and stable replay, and leaves only the initializer's data variable open. The named initializer must be a direct gate whose transparent body is a tuple ending in null. Its own sample fixes data; ordinary inference, product and whole-gate fits, pending-tag and finalization checks, and a fresh ordinary `main` inference must all succeed before the ordinary kind and graph checks publish anything. Aliases, non-main libraries, simultaneous unresolved subscriptions, nonempty subscriptions and unrelated pending tags decline.
134
136
135A component-local recovery sequence handles a recognized direct program whose context-dependent definitions cannot resolve ordinarily. Update first derives its result from its inferred two-slot sample and canonical `cmd`/`list` templates. Subscription recovery then checks the same message/model relation and the canonical fourth slot. A narrow keyed-view proof finds exactly one direct `each (turn xs mapper)` site, derives the mapper from the actual `each` and `turn` schemes, and recovers the view before the existing initializer and ordinary `main` retries. Every recovered scheme must be closed and freshly allocated above the current environment and prelude IDs. A source screen skips doomed ordinary attempts only for this keyed shape and never skips final `main` inference; non-gate views and aliases decline without crashing. Final publication still requires all module definitions, the program-mold and gate-free checks, and request-value watchability.
137A component-local recovery sequence handles a recognized direct program whose context-dependent definitions cannot resolve ordinarily. Update first derives its result from its inferred two-slot sample and canonical `cmd`/`list` templates. Subscription recovery then checks the same message/model relation and the canonical fourth slot. A narrow keyed-view proof finds exactly one direct `each (turn xs mapper)` site, derives the mapper from the actual `each` and `turn` schemes, and recovers the view before the existing initializer and ordinary `main` retries. Every recovered scheme must be closed and freshly allocated above the current environment and prelude IDs. A source screen skips doomed ordinary attempts only for this keyed shape and never skips final `main` inference; non-gate views and aliases decline without crashing. Final publication still requires all module definitions, the program-mold and gate-free checks, and request-value watchability.
docs/development.md+2
@@ -91,6 +91,7 @@ git clone https://github.com/urbit/tools /root/src/tools
91| `tools/type-todo-partial-test` | Reports partial inference of the unchanged todo island through the real API and readers under eval and on the ship |
91| `tools/type-todo-partial-test` | Reports partial inference of the unchanged todo island through the real API and readers under eval and on the ship |
92| `tools/type-todo-api-test` | Exercises the actual todo API through the real Hoon readers and partial checker |
92| `tools/type-todo-api-test` | Exercises the actual todo API through the real Hoon readers and partial checker |
93| `tools/type-direct-gate-test` | Checks a synthetic source-backed program report and direct gate provenance under eval and on the ship |
93| `tools/type-direct-gate-test` | Checks a synthetic source-backed program report and direct gate provenance under eval and on the ship |
94+| `tools/type-page-main-test` | Fully checks byte-identical static and direct data pages through real readers, exact main types, downstream imports and rejection guards under eval and on the ship |
94| `tools/type-program-main-test` | Checks the narrow closed subscription-free program-main interface, downstream import and rejection guards under eval and on the ship |
95| `tools/type-program-main-test` | Checks the narrow closed subscription-free program-main interface, downstream import and rejection guards under eval and on the ship |
95| `tools/type-program-init-test` | Checks transactional direct-program initializer recovery, exact exported/imported types and fail-closed boundaries under eval and on the ship |
96| `tools/type-program-init-test` | Checks transactional direct-program initializer recovery, exact exported/imported types and fail-closed boundaries under eval and on the ship |
96| `tools/type-program-update-test` | Checks transactional direct-program update result recovery, union/boolean context, exact exported/imported types and fail-closed boundaries under eval and on the ship |
97| `tools/type-program-update-test` | Checks transactional direct-program update result recovery, union/boolean context, exact exported/imported types and fail-closed boundaries under eval and on the ship |
@@ -145,6 +146,7 @@ tools/type-qath-guards-test # qath source and unfolding guards pass
145tools/type-todo-partial-test # unchanged todo partial report and reader failure controls
146tools/type-todo-partial-test # unchanged todo partial report and reader failure controls
146tools/type-todo-api-test # real todo API partial inference and reader guards pass
147tools/type-todo-api-test # real todo API partial inference and reader guards pass
147tools/type-direct-gate-test # direct gate source and provenance guards pass
148tools/type-direct-gate-test # direct gate source and provenance guards pass
149+tools/type-page-main-test # static/data pages, exact types, imports and rejection guards pass
148tools/type-program-main-test # closed program-main interface and rejection guards pass
150tools/type-program-main-test # closed program-main interface and rejection guards pass
149tools/type-program-init-test # transactional direct-program initializer context guards pass
151tools/type-program-init-test # transactional direct-program initializer context guards pass
150tools/type-program-update-test # transactional direct-program update context guards pass
152tools/type-program-update-test # transactional direct-program update context guards pass
docs/status.md+5−2
@@ -47,6 +47,8 @@ The partial checker can also match a directly named, checked root gate against a
47
47
48A narrow full-module path now checks direct four-wing `(program init update view subs)` definitions named `main`. It requires the canonical prepared `program` value and mold, reuses ordinary source-backed program fitting, and publishes only after all ordinary module and request-graph checks. The inferred `main` must be closed and invariantly match the instantiated `program` mold. Initializer data and model must be closed and gate-free with `%yes` verdicts inside a shared 256-node budget. `subs` may syntactically return null through transparent forms, or one direct imported request watch after the request graph merges. The latter is only an unfaced two-item `[%watch (case.face |=%callback)]` singleton: the imported member must have the exact plain derived-request scheme, a unique merged request must match the API path and both case names and occur in the interface's own graph, the API source must be identical, and scry, diffs and parameters must be absent. Pure or hand-built values, helpers, aliases, re-exports, typed paths, folds, multiple watches and unknown or ambiguous evidence remain rejected. Checked interfaces retain source evidence and support ordinary downstream imports; no program execution or runtime watching is claimed. A transactional extension can recover a direct initializer whose transparent tuple body ends in null when update, view and subs are already resolved. It derives the expected gate through the ordinary positional program fit with fresh IDs and stable replay, then re-infers the actual initializer and `main` under all ordinary finalization and kind checks. Non-main libraries, aliases, nonempty or simultaneously unresolved subscriptions, and unrelated pending tags remain rejected.
48A narrow full-module path now checks direct four-wing `(program init update view subs)` definitions named `main`. It requires the canonical prepared `program` value and mold, reuses ordinary source-backed program fitting, and publishes only after all ordinary module and request-graph checks. The inferred `main` must be closed and invariantly match the instantiated `program` mold. Initializer data and model must be closed and gate-free with `%yes` verdicts inside a shared 256-node budget. `subs` may syntactically return null through transparent forms, or one direct imported request watch after the request graph merges. The latter is only an unfaced two-item `[%watch (case.face |=%callback)]` singleton: the imported member must have the exact plain derived-request scheme, a unique merged request must match the API path and both case names and occur in the interface's own graph, the API source must be identical, and scry, diffs and parameters must be absent. Pure or hand-built values, helpers, aliases, re-exports, typed paths, folds, multiple watches and unknown or ambiguous evidence remain rejected. Checked interfaces retain source evidence and support ordinary downstream imports; no program execution or runtime watching is claimed. A transactional extension can recover a direct initializer whose transparent tuple body ends in null when update, view and subs are already resolved. It derives the expected gate through the ordinary positional program fit with fresh IDs and stable replay, then re-infers the actual initializer and `main` under all ordinary finalization and kind checks. Non-main libraries, aliases, nonempty or simultaneously unresolved subscriptions, and unrelated pending tags remain rejected.
49
49
50+Static and direct data-page `main` checking now completes the unchanged about page, blog index and todo index through the real Hoon readers. Static generic element mains specialize to closed canonical `(element !!)`; direct pages require a closed invariant `page` type and a direct plain derived-request load. Page loads reuse the strict source/member/evidence identity checks used by program watches, rejecting aliases, indirect/hand-built/pure/unknown requests, typed paths, diffs and store paths. The shared eval and ship fixture asserts exact main types and structural downstream imports. Rendering, runtime loading and request execution remain unimplemented.
51+
50The component-local program recovery now covers update, subscriptions, one direct keyed `each`/`turn` mapper and its view, then the existing initializer and ordinary `main` path. Each recovered scheme is closed and freshly allocated; a keyed source screen avoids repeated doomed ordinary attempts without skipping final `main`, and non-gate views decline safely. Canonical `element` recursion and exact singleton-case list fits are narrowly guarded, with successful ordinary fits taking precedence. The byte-identical unchanged todo now passes both detailed and legacy full-module checks with an exact closed `main` type and structural downstream import. Wrong models, messages, results, mappers, keys, aliases, non-gate views and watches fail closed. This is static checking only: no Jibb execution or runtime watching is claimed.
52The component-local program recovery now covers update, subscriptions, one direct keyed `each`/`turn` mapper and its view, then the existing initializer and ordinary `main` path. Each recovered scheme is closed and freshly allocated; a keyed source screen avoids repeated doomed ordinary attempts without skipping final `main`, and non-gate views decline safely. Canonical `element` recursion and exact singleton-case list fits are narrowly guarded, with successful ordinary fits taking precedence. The byte-identical unchanged todo now passes both detailed and legacy full-module checks with an exact closed `main` type and structural downstream import. Wrong models, messages, results, mappers, keys, aliases, non-gate views and watches fail closed. This is static checking only: no Jibb execution or runtime watching is claimed.
51
53
52The API path analyzer can retain its source, complete prepared prelude, reader results and resolved dependencies as evidence, and classify a unique declaration as watchable, not watchable or unknown. This remains declaration evidence rather than proof about an arbitrary request value. The narrow direct-program check above composes it with final value syntax, resolved imports and merged evidence for one plain request. Prepared artifacts and reader results remain trusted host inputs.
54The API path analyzer can retain its source, complete prepared prelude, reader results and resolved dependencies as evidence, and classify a unique declaration as watchable, not watchable or unknown. This remains declaration evidence rather than proof about an arbitrary request value. The narrow direct-program check above composes it with final value syntax, resolved imports and merged evidence for one plain request. Prepared artifacts and reader results remain trusted host inputs.
@@ -100,7 +102,7 @@ What the plan asks for, and where it is:
100Besides the plan's list:
102Besides the plan's list:
101
103
102- 660 golden cases: 440 that parse and 220 that fail, each failure at a stated line and column. One failing case has an unclear expectation (corpus question 3). The newer type-output cases exercise only the partial inference fragment. Each case's `about.txt` quotes the doc's words it tests.
104- 660 golden cases: 440 that parse and 220 that fail, each failure at a stated line and column. One failing case has an unclear expectation (corpus question 3). The newer type-output cases exercise only the partial inference fragment. Each case's `about.txt` quotes the doc's words it tests.
103−- The 660-case corpus passed under `urbit eval` in 107.0 s while other checks were running. The fake ship passed all 712 tests, including the source-overlap, structural-overlap, generic-union source and alias-overlap, full-module overlap, deferred-context, generic-recursion, generic-construction, generic-pattern, deferred-inference, empty-mold and cell-branch fixtures, importer, importer/checker and mark/checker bridges, module scope, unification, hole, qath, ask, default-wrapper importer, projection, arithmetic, generic-alias, positional-face, API-evidence, request, closed-gate, todo API, constructor, sub, nested-union, gate-free and direct-gate guards and the unchanged todo report. Desk sync took 102.0 s while other checks were running. The importer golden compares 221 projections; `tools/clay-fixtures` builds the example Hoon fixtures through Clay, checks a recursive Ford import's origin, and passes Clay-built types through the Clay-built importer and partial checker.
105+- The 660-case corpus passed under `urbit eval` in 98.9 s while other checks were running. The fake ship passed all 713 tests, including the source-overlap, structural-overlap, generic-union source and alias-overlap, full-module overlap, deferred-context, generic-recursion, generic-construction, generic-pattern, deferred-inference, empty-mold and cell-branch fixtures, importer, importer/checker and mark/checker bridges, module scope, unification, hole, qath, ask, default-wrapper importer, projection, arithmetic, generic-alias, positional-face, API-evidence, request, closed-gate, todo API, constructor, sub, nested-union, gate-free and direct-gate guards and the unchanged todo report. Desk sync took 104.9 s while other checks were running. The importer golden compares 221 projections; `tools/clay-fixtures` builds the example Hoon fixtures through Clay, checks a recursive Ford import's origin, and passes Clay-built types through the Clay-built importer and partial checker.
104- `tools/testdata/selftest` (22 checks), `tools/testdata/ship-selftest` and `tools/testdata/clay-fixtures-selftest` test the tools themselves; the last checks temporary Clay fixture cleanup and signal handling without a ship.
106- `tools/testdata/selftest` (22 checks), `tools/testdata/ship-selftest` and `tools/testdata/clay-fixtures-selftest` test the tools themselves; the last checks temporary Clay fixture cleanup and signal handling without a ship.
105- Two independent audits before 7c739b7, one of which compared the library with a reference parser written in Python from the doc over about 19,000 generated programs. Before ab797a9, a second Python parser, written from the decisions, agreed with the library on 8,000 generated programs and 900 hand-written probes.
107- Two independent audits before 7c739b7, one of which compared the library with a reference parser written in Python from the doc over about 19,000 generated programs. Before ab797a9, a second Python parser, written from the decisions, agreed with the library on 8,000 generated programs and 900 hand-written probes.
106- Speed: starting `urbit eval` and building the library takes about 8 s. After that, a 2,000-line module of 400 definitions parses in about a second. Loops over definitions, arms, lines and items grow no stack; nesting costs a few stack frames a level, and 100,000 nested brackets still parse (library section 3).
108- Speed: starting `urbit eval` and building the library takes about 8 s. After that, a 2,000-line module of 400 definitions parses in about a second. Loops over definitions, arms, lines and items grow no stack; nesting costs a few stack frames a level, and 100,000 nested brackets still parse (library section 3).
@@ -220,7 +222,8 @@ Besides the plan's list:
220| 642d2c0 | 2026-11-01 | Prove direct request-value watchability for program subscriptions |
222| 642d2c0 | 2026-11-01 | Prove direct request-value watchability for program subscriptions |
221| 2e4d582 | 2026-11-01 | Add the source-backed UI, program and page prelude slice |
223| 2e4d582 | 2026-11-01 | Add the source-backed UI, program and page prelude slice |
222| a163965 | 2026-11-01 | Recover direct-program update result contexts |
224| a163965 | 2026-11-01 | Recover direct-program update result contexts |
223−| This change | 2026-11-01 | Check the unchanged todo island through component recovery |
225+| 46748b0 | 2026-11-01 | Check the unchanged todo island through component recovery |
226+| This change | 2026-11-01 | Check static and direct data-page mains |
224
227
225## Checking it
228## Checking it
226
229
jibb-dev/lib/jibb.hoon+136−21
@@ -12533,6 +12533,27 @@
12533 ?~ subs ~
12533 ?~ subs ~
12534 `[u.init u.update u.view u.subs]
12534 `[u.init u.update u.view u.subs]
12535::
12535::
12536+:: A data page is only the direct two-component source call. Grouping is
12537+:: transparent, but page aliases, qualified wings and other kinds are not.
12538+++ page-main-parts
12539+ |= d=def
12540+ ^- (unit [load=term view=term])
12541+ ?. ?=(%value -.d) ~
12542+ |-
12543+ ?: ?=(%group -.val.d) $(val.d inner.val.d)
12544+ ?. ?=(%call -.val.d) ~
12545+ =/ head=(unit term) (program-main-wing head.val.d)
12546+ ?~ head ~
12547+ ?. =(%page u.head) ~
12548+ ?. ?=([* * ~] args.val.d) ~
12549+ =/ args=(list expr) args.val.d
12550+ ?> ?=([* * ~] args)
12551+ =/ load=(unit term) (program-main-wing i.args)
12552+ =/ view=(unit term) (program-main-wing i.t.args)
12553+ ?~ load ~
12554+ ?~ view ~
12555+ `[u.load u.view]
12556+::
12536++ program-empty-null
12557++ program-empty-null
12537 |= e=expr
12558 |= e=expr
12538 ^- ?
12559 ^- ?
@@ -13497,36 +13518,45 @@
13497 =/ callback=expr (program-transparent i.args.request)
13518 =/ callback=expr (program-transparent i.args.request)
13498 ?. ?=(%ctor -.callback) |
13519 ?. ?=(%ctor -.callback) |
13499 ?. ?=(%term form.tag.callback) |
13520 ?. ?=(%term form.tag.callback) |
13521+ ?=(^ (derived-request-member case face imports graph prelude))
13522+::
13523+:: Exact derived-member and request-evidence identity, shared by direct
13524+:: program watches and direct data-page loads. Only a plain request in the
13525+:: selected interface's own source-backed graph can establish this shape.
13526+++ derived-request-member
13527+ |= [case=term face=term imports=ijimports graph=irequest-graph prelude=ifile]
13528+ ^- (unit ischeme)
13500 =/ selected=(list [face=term interface=ifile])
13529 =/ selected=(list [face=term interface=ifile])
13501 (skim imports |=(j=[face=term interface=ifile] =(face face.j)))
13530 (skim imports |=(j=[face=term interface=ifile] =(face face.j)))
13502− ?. ?=([* ~] selected) |
13531+ ?. ?=([* ~] selected) ~
13503 =/ iface=ifile interface.i.selected
13532 =/ iface=ifile interface.i.selected
13504 =/ member=(unit ischeme) (ienv-lookup case values.iface)
13533 =/ member=(unit ischeme) (ienv-lookup case values.iface)
13505− ?~ member |
13534+ ?~ member ~
13506− ?~ source.iface |
13535+ ?~ source.iface ~
13507 =/ evidence=(list irequest-evidence)
13536 =/ evidence=(list irequest-evidence)
13508 (skim requests.graph |=(r=irequest-evidence &(=(api-at.r at.iface) =(name.r case))))
13537 (skim requests.graph |=(r=irequest-evidence &(=(api-at.r at.iface) =(name.r case))))
13509− ?. ?=([* ~] evidence) |
13538+ ?. ?=([* ~] evidence) ~
13510 =/ request-evidence=irequest-evidence i.evidence
13539 =/ request-evidence=irequest-evidence i.evidence
13511− ?. =(u.source.iface api-source.request-evidence) |
13540+ ?. =(u.source.iface api-source.request-evidence) ~
13512− ?. =(name.path.path.request-evidence case) |
13541+ ?. =(name.path.path.request-evidence case) ~
13513− ?~ graph.iface |
13542+ ?~ graph.iface ~
13514− ?. (lien requests.u.graph.iface |=(r=irequest-evidence =(r request-evidence))) |
13543+ ?. (lien requests.u.graph.iface |=(r=irequest-evidence =(r request-evidence))) ~
13515− ?. ?=(~ scry.path.path.request-evidence) |
13544+ ?. ?=(~ scry.path.path.request-evidence) ~
13516− ?. &(?=(~ diffs.path.request-evidence) ?=(~ diffs.path.path.request-evidence)) |
13545+ ?. &(?=(~ diffs.path.request-evidence) ?=(~ diffs.path.path.request-evidence)) ~
13517− ?: (lien segments.path.path.request-evidence |=(p=pseg !?=(%knot -.p))) |
13546+ ?: (lien segments.path.path.request-evidence |=(p=pseg !?=(%knot -.p))) ~
13518− ?. ?=([@ ~] vars.u.member) |
13547+ ?. ?=([@ ~] vars.u.member) ~
13519 =/ message=@ud i.vars.u.member
13548 =/ message=@ud i.vars.u.member
13520− ?. ?=(%fn -.typ.u.member) |
13549+ ?. ?=(%fn -.typ.u.member) ~
13521− ?. ?=(%fn -.sample.typ.u.member) |
13550+ ?. ?=(%fn -.sample.typ.u.member) ~
13522− ?. =(sample.sample.typ.u.member full.path.request-evidence) |
13551+ ?. =(sample.sample.typ.u.member full.path.request-evidence) ~
13523− ?. =(product.sample.typ.u.member [%var message]) |
13552+ ?. =(product.sample.typ.u.member [%var message]) ~
13524 =/ prep=iprelude [prelude prelude-values.graph prelude-molds.graph]
13553 =/ prep=iprelude [prelude prelude-values.graph prelude-molds.graph]
13525 =/ ask=(unit ischeme) (request-compatible-ask prep)
13554 =/ ask=(unit ischeme) (request-compatible-ask prep)
13526− ?~ ask |
13555+ ?~ ask ~
13527 =/ result=(unit itype) (template-apply u.ask ~[[%var message]])
13556 =/ result=(unit itype) (template-apply u.ask ~[[%var message]])
13528− ?~ result |
13557+ ?~ result ~
13529− =(product.typ.u.member u.result)
13558+ ?. =(product.typ.u.member u.result) ~
13559+ member
13530::
13560::
13531++ program-main-context
13561++ program-main-context
13532 |= [main=def ds=(list def) resolution=iresolved initial=istate]
13562 |= [main=def ds=(list def) resolution=iresolved initial=istate]
@@ -13540,6 +13570,77 @@
13540 (add-supported-defs-core ~[main] ds checked heads env.resolution molds.initial globals.initial externals.initial surs.initial mark-externals.initial marks.initial jimports.initial reserved-molds.initial at.initial prelude.initial artifacts.resolution grec-index.initial)
13570 (add-supported-defs-core ~[main] ds checked heads env.resolution molds.initial globals.initial externals.initial surs.initial mark-externals.initial marks.initial jimports.initial reserved-molds.initial at.initial prelude.initial artifacts.resolution grec-index.initial)
13541 [env.completed grouped.resolution artifacts.completed]
13571 [env.completed grouped.resolution artifacts.completed]
13542::
13572::
13573+:: A static main may retain a generic element message in its ordinary
13574+:: scheme. Instantiate that scheme, specialize it to canonical element !!,
13575+:: and publish only the resulting closed scheme.
13576+++ static-main-specialized
13577+ |= [env=ienv prelude=(unit ifile)]
13578+ ^- (unit ischeme)
13579+ ?~ prelude ~
13580+ =/ element=(unit ischeme) (find-jibb-mold %element u.prelude)
13581+ ?~ element ~
13582+ =/ main=(unit ischeme) (ienv-lookup %main env)
13583+ ?~ main ~
13584+ =/ canonical=(unit itype) (template-apply u.element ~[[%empty ~]])
13585+ ?~ canonical ~
13586+ =/ instance=[typ=itype state=istate] (instantiate-scheme u.main *istate)
13587+ =/ fit=ifit (itype-fit typ.instance u.canonical ~ next.state.instance)
13588+ ?. ?=(%ok -.fit) ~
13589+ =/ closed=itype (itype-normalize typ.instance sub.fit)
13590+ ?^ (itype-vars closed) ~
13591+ ?: (itype-has-tag closed) ~
13592+ ?. ?=(%union -.closed) ~
13593+ ?. ?=(%union -.u.canonical) ~
13594+ ?. &(=(origin.closed origin.u.canonical) =(args.closed args.u.canonical)) ~
13595+ =/ exact=ifit (direct-invariant closed u.canonical ~ next.fit)
13596+ ?. ?=(%ok -.exact) ~
13597+ `[u.canonical ~]
13598+::
13599+:: The load of a direct data page must itself be a direct gate whose
13600+:: transparent body is one plain derived request with a wing or ctor reply.
13601+++ page-load-derived
13602+ |= [name=term ds=(list def) imports=ijimports graph=irequest-graph prelude=ifile]
13603+ ^- ?
13604+ =/ loads=(list def)
13605+ (skim ds |=(d=def &(?=(%value -.d) =(name name.d))))
13606+ ?. ?=([* ~] loads) |
13607+ =/ load=def i.loads
13608+ ?> ?=(%value -.load)
13609+ ?. |(?=(%gate -.val.load) ?=(%gate-wide -.val.load)) |
13610+ =/ body=expr
13611+ ?- -.val.load
13612+ %gate body.val.load
13613+ %gate-wide body.val.load
13614+ * !!
13615+ ==
13616+ =. body (program-transparent body)
13617+ ?. ?=(%call -.body) |
13618+ ?. ?=([* ~] args.body) |
13619+ =/ head=expr (program-transparent head.body)
13620+ ?. ?=(%wing -.head) |
13621+ ?. ?=([@ @ ~] names.head) |
13622+ =/ reply=expr (program-transparent i.args.body)
13623+ ?. ?| &(?=(%wing -.reply) ?=([@ ~] names.reply))
13624+ &(?=(%ctor -.reply) ?=(%term form.tag.reply))
13625+ ==
13626+ |
13627+ ?=(^ (derived-request-member i.names.head i.t.names.head imports graph prelude))
13628+::
13629+++ page-main-closed
13630+ |= [parts=[load=term view=term] ds=(list def) env=ienv prelude=(unit ifile) imports=ijimports graph=irequest-graph]
13631+ ^- ?
13632+ ?~ prelude |
13633+ =/ page=(unit ischeme) (find-jibb-mold %page u.prelude)
13634+ ?~ page |
13635+ =/ main=(unit ischeme) (ienv-lookup %main env)
13636+ ?~ main |
13637+ ?. ?=(~ vars.u.main) |
13638+ ?. ?=(~ (itype-vars typ.u.main)) |
13639+ =/ instance=[typ=itype state=istate] (instantiate-scheme u.page *istate)
13640+ =/ main-fit=ifit (direct-invariant typ.u.main typ.instance ~ next.state.instance)
13641+ ?. ?=(%ok -.main-fit) |
13642+ (page-load-derived load.parts ds imports graph u.prelude)
13643+::
13543++ program-main-closed
13644++ program-main-closed
13544 |= [parts=[init=term update=term view=term subs=term] ds=(list def) env=ienv prelude=(unit ifile) imports=ijimports graph=irequest-graph]
13645 |= [parts=[init=term update=term view=term subs=term] ds=(list def) env=ienv prelude=(unit ifile) imports=ijimports graph=irequest-graph]
13545 ^- ?
13646 ^- ?
@@ -13617,7 +13718,9 @@
13617 =/ main=(unit def) (program-main-def defs.mod)
13718 =/ main=(unit def) (program-main-def defs.mod)
13618 =/ main-parts=(unit [init=term update=term view=term subs=term])
13719 =/ main-parts=(unit [init=term update=term view=term subs=term])
13619 ?~(main ~ (program-main-parts u.main))
13720 ?~(main ~ (program-main-parts u.main))
13721+ =/ page-parts=(unit [load=term view=term])
13722+ ?~(main ~ (page-main-parts u.main))
13620− ?. ?~(main & &(!?=([%ui %stores *] at) ?=(^ main-parts)))
13723+ ?: &(?=([%ui %stores *] at) ?=(^ main))
13621 [%| [(crip "module kind needs unsupported inference: {(spud at)}") ~]]
13724 [%| [(crip "module kind needs unsupported inference: {(spud at)}") ~]]
13622 =/ js=jimports-detailed-result (resolve-jibb-imports-detailed imports.mod externals mark-externals modules active prelude-values prelude-molds cache prelude)
13725 =/ js=jimports-detailed-result (resolve-jibb-imports-detailed imports.mod externals mark-externals modules active prelude-values prelude-molds cache prelude)
13623 ?: ?=(%| -.js) [%| p.js]
13726 ?: ?=(%| -.js) [%| p.js]
@@ -13690,7 +13793,19 @@
13690 =/ initial=irequest-graph [(silt `(list path)`~[at]) ~ modules prelude prelude-values prelude-molds externals mark-externals]
13793 =/ initial=irequest-graph [(silt `(list path)`~[at]) ~ modules prelude prelude-values prelude-molds externals mark-externals]
13691 =/ graph=(unit irequest-graph) (request-graph-merge resolved.p.js initial (add 1 (lent resolved.p.js)))
13794 =/ graph=(unit irequest-graph) (request-graph-merge resolved.p.js initial (add 1 (lent resolved.p.js)))
13692 ?~ graph [%| ['module request evidence needs unsupported inference' ~]]
13795 ?~ graph [%| ['module request evidence needs unsupported inference' ~]]
13796+ =/ static-main=(unit ischeme)
13797+ ?~ main ~
13798+ ?^ main-parts ~
13799+ ?^ page-parts ~
13800+ (static-main-specialized env prelude)
13801+ =? env ?=(^ static-main)
13802+ [[%main u.static-main] (skim env |=(entry=[name=term scheme=ischeme] !=(%main name.entry)))]
13803+ =/ kind-ok=?
13804+ ?~ main &
13693− ?. ?~(main & ?~(main-parts | (program-main-closed u.main-parts defs.mod env prelude resolved.p.js u.graph)))
13805+ ?^ main-parts (program-main-closed u.main-parts defs.mod env prelude resolved.p.js u.graph)
13806+ ?^ page-parts (page-main-closed u.page-parts defs.mod env prelude resolved.p.js u.graph)
13807+ ?=(^ static-main)
13808+ ?. kind-ok
13694 [%| [(crip "module kind needs unsupported inference: {(spud at)}") ~]]
13809 [%| [(crip "module kind needs unsupported inference: {(spud at)}") ~]]
13695 =/ iface=ifile [env p.mts at ~ artifacts.resolution ~ `src graph]
13810 =/ iface=ifile [env p.mts at ~ artifacts.resolution ~ `src graph]
13696 [%& [iface [[at iface] cache.p.js]]]
13811 [%& [iface [[at iface] cache.p.js]]]
tests/lib/jibb-page-main.hoonnew file+168
@@ -0,0 +1,168 @@
1+:: Static and direct data-page kind checks, without rendering or execution.
2+/+ jibb, jibb-import
3+/* prelude %jibb /lib/jibb/prelude/jibb
4+/* internal %jibb /lib/jibb/internal/jibb
5+/* sur %txt /tests/type-api-paths/todo-sur/txt
6+/* action %txt /tests/type-api-paths/todo-action/txt
7+/* todos %txt /tests/type-api-paths/todos/txt
8+/* update %txt /tests/type-api-paths/todo-update/txt
9+/* item %txt /tests/type-api-paths/todo-item/txt
10+/* todo-api %txt /tests/type-page-main/todo-api/txt
11+/* todo-island %txt /tests/type-page-main/todo-island/txt
12+/* blog-sur %txt /tests/type-page-main/blog-sur/txt
13+/* posts %txt /tests/type-page-main/posts-mark/txt
14+/* blog-api %txt /tests/type-page-main/blog-api/txt
15+/* card %txt /tests/type-page-main/card/txt
16+/* about %txt /tests/type-page-main/static/txt
17+/* blog-index %txt /tests/type-page-main/blog-index/txt
18+/* index %txt /tests/type-page-main/index/txt
19+/* ctor-page %txt /tests/type-page-main/ctor-page/txt
20+/* wrong-static %txt /tests/type-page-main/wrong-static/txt
21+/* arbitrary-main %txt /tests/type-page-main/arbitrary-main/txt
22+/* page-alias %txt /tests/type-page-main/page-alias/txt
23+/* indirect-load %txt /tests/type-page-main/indirect-load/txt
24+/* pure-load %txt /tests/type-page-main/pure-load/txt
25+/* hand-built-load %txt /tests/type-page-main/hand-built-load/txt
26+/* typed-load %txt /tests/type-page-main/typed-load/txt
27+/* diff-load %txt /tests/type-page-main/diff-load/txt
28+|%
29+++ text
30+ |= lines=wain
31+ ^- @t
32+ (rap 3 (turn lines |=(line=@t (cat 3 line '\0a'))))
33+::
34+++ page-main-detailed
35+ |= [at=path src=@t externals=iexternals:jibb marks=imark-externals:jibb modules=jbundle:jibb prepared=iprelude:jibb]
36+ ^- jfile-detailed-result:jibb
37+ (infer-jibb-file-detailed:jibb at src externals marks modules ~[at] values.prepared molds.prepared ~ `iface.prepared)
38+::
39+++ page-main-reader-inputs
40+ |= [sur=@t action=@t todos=@t update=@t item=@t blog-sur=@t posts=@t]
41+ ^- [marks=imark-externals:jibb externals=iexternals:jibb]
42+ =/ todo=type -:(ride -:!>(..zuse) sur)
43+ =/ todo-subject=type [%cell [%face %todo todo] -:!>(..zuse)]
44+ =/ ma=* (read-mark:jibb-import %todo-action -:(ride todo-subject action))
45+ =/ mt=* (read-mark:jibb-import %todos -:(ride todo-subject todos))
46+ =/ mu=* (read-mark:jibb-import %todo-update -:(ride todo-subject update))
47+ =/ mi=* (read-mark:jibb-import %todo-item -:(ride todo-subject item))
48+ =/ blog=type -:(ride -:!>(..zuse) blog-sur)
49+ =/ blog-subject=type [%cell [%face %blog blog] -:!>(..zuse)]
50+ =/ mp=* (read-mark:jibb-import %posts -:(ride blog-subject posts))
51+ =/ item-input=* (read-named:jibb-import %todo %item todo)
52+ =/ post-input=* (read-named:jibb-import %blog %post blog)
53+ :_ ~[[%todo %item item-input] [%blog %post post-input]]
54+ ~[[%todo-action ma] [%todos mt] [%todo-update mu] [%todo-item mi] [%posts mp]]
55+::
56+++ page-main-positive
57+ |= [label=tape at=path src=@t expected=@t consumer-at=path consumer=@t face=term externals=iexternals:jibb marks=imark-externals:jibb modules=jbundle:jibb prepared=iprelude:jibb]
58+ ^- (list tape)
59+ =/ detailed=jfile-detailed-result:jibb
60+ (page-main-detailed at src externals marks modules prepared)
61+ ?: ?=(%| -.detailed)
62+ ~[(weld label (weld ": rejected: " (trip message.p.detailed)))]
63+ =/ checked=ifile:jibb iface.p.detailed
64+ ?~ graph.checked ~[(weld label ": request graph is missing")]
65+ =/ main=(unit ischeme:jibb) (ienv-lookup:jibb %main values.checked)
66+ ?~ main ~[(weld label ": main is missing")]
67+ ?. &(?=(~ vars.u.main) ?=(~ (itype-vars:jibb typ.u.main)))
68+ ~[(weld label ": main is not closed")]
69+ =/ actual=@t (crip (show-itype:jibb typ.u.main ~))
70+ ?. =(actual expected)
71+ ~[(weld label (weld ": main type was " (trip actual)))]
72+ =/ imported=jfile-detailed-result:jibb
73+ (page-main-detailed consumer-at consumer externals marks modules prepared)
74+ ?: ?=(%| -.imported)
75+ ~[(weld label (weld ": downstream import rejected: " (trip message.p.imported)))]
76+ =/ use=(unit ischeme:jibb) (ienv-lookup:jibb %imported values.iface.p.imported)
77+ ?~ use ~[(weld label ": downstream main is missing")]
78+ =/ expected-import=itype:jibb
79+ (itype-in-import:jibb typ.u.main checked face `iface.prepared)
80+ ?. &(?=(~ vars.u.use) =(typ.u.use expected-import))
81+ ~[(weld label ": downstream main type differs")]
82+ ~
83+::
84+++ page-main-negative
85+ |= [label=@t at=path src=@t externals=iexternals:jibb marks=imark-externals:jibb modules=jbundle:jibb prepared=iprelude:jibb]
86+ ^- (list tape)
87+ =/ bad=jfile-detailed-result:jibb
88+ (page-main-detailed at src externals marks modules prepared)
89+ ?: ?=(%& -.bad) ~[(weld (trip label) ": accepted")]
90+ ~
91+::
92+++ page-main-errors
93+ |= [public=@t private=@t sur=@t action=@t todos=@t update=@t item=@t todo-api=@t todo-island=@t blog-sur=@t posts=@t blog-api=@t card=@t about=@t blog-index=@t index=@t ctor-page=@t negatives=(list [label=@t src=@t])]
94+ ^- (list tape)
95+ =/ prepared=(unit iprelude:jibb)
96+ (prepare-prelude-with-internal:jibb public private)
97+ ?~ prepared ~["page prelude did not prepare"]
98+ =/ inputs=[marks=imark-externals:jibb externals=iexternals:jibb]
99+ (page-main-reader-inputs sur action todos update item blog-sur posts)
100+ =/ modules=jbundle:jibb
101+ :~ [/ui/api/todo/jibb todo-api]
102+ [/ui/todo-list/jibb todo-island]
103+ [/ui/api/blog/jibb blog-api]
104+ [/ui/components/card/jibb card]
105+ [/ui/about/jibb about]
106+ [/ui/blog/index/jibb blog-index]
107+ [/ui/index/jibb index]
108+ [/tests/page-main/ctor/jibb ctor-page]
109+ ==
110+ =/ about-consumer=@t '/= about-page /ui/about\0a++ imported main.about-page\0a'
111+ =/ blog-consumer=@t '/= blog-page /ui/blog/index\0a++ imported main.blog-page\0a'
112+ =/ index-consumer=@t '/= index-page /ui/index\0a++ imported main.index-page\0a'
113+ =/ ctor-consumer=@t '/= ctor-page /tests/page-main/ctor\0a++ imported main.ctor-page\0a'
114+ =/ positives=(list (list tape))
115+ :~ (page-main-positive "about" /ui/about/jibb about '(element !!)' /tests/page-main/about-consumer/jibb about-consumer %about-page externals.inputs marks.inputs modules u.prepared)
116+ (page-main-positive "blog index" /ui/blog/index/jibb blog-index '[load=$-(~ (ask (list [title=@t slug=@ta]))) view=$-((list [title=@t slug=@ta]) (element !!))]' /tests/page-main/blog-consumer/jibb blog-consumer %blog-page externals.inputs marks.inputs modules u.prepared)
117+ (page-main-positive "index" /ui/index/jibb index '[load=$-(~ (ask (list [id=@ud [done=? text=@t]]))) view=$-((list [id=@ud [done=? text=@t]]) (element !!))]' /tests/page-main/index-consumer/jibb index-consumer %index-page externals.inputs marks.inputs modules u.prepared)
118+ (page-main-positive "constructor reply" /tests/page-main/ctor/jibb ctor-page '[load=$-(~ (ask [%got todos=(list [id=@ud [done=? text=@t]])])) view=$-([%got todos=(list [id=@ud [done=? text=@t]])] (element !!))]' /tests/page-main/ctor-consumer/jibb ctor-consumer %ctor-page externals.inputs marks.inputs modules u.prepared)
119+ ==
120+ =/ failures=(list tape) (zing positives)
121+ ?^ failures failures
122+ =/ at=path /ui/index/jibb
123+ =/ checked-result=jfile-detailed-result:jibb
124+ (page-main-detailed at index externals.inputs marks.inputs modules u.prepared)
125+ ?: ?=(%| -.checked-result) ~["index disappeared before evidence controls"]
126+ =/ checked=ifile:jibb iface.p.checked-result
127+ ?~ graph.checked ~["index graph disappeared before evidence controls"]
128+ =/ parsed (parse:jibb at index)
129+ ?: ?=(%| -.parsed) ~["index source no longer parses"]
130+ =/ main=(unit def:jibb) (program-main-def:jibb defs.p.parsed)
131+ ?~ main ~["index main definition is missing"]
132+ =/ parts=(unit [load=term view=term]) (page-main-parts:jibb u.main)
133+ ?~ parts ~["index page parts are missing"]
134+ =/ others=(list irequest-evidence:jibb)
135+ (skim requests.u.graph.checked |=(r=irequest-evidence:jibb !&(=(api-at.r /ui/api/todo/jibb) =(%todos name.r))))
136+ =/ missing=irequest-graph:jibb u.graph.checked(requests others)
137+ =/ resolved=ijimports:jibb
138+ =/ found=(unit ifile:jibb)
139+ =/ api=jfile-detailed-result:jibb
140+ (page-main-detailed /ui/api/todo/jibb todo-api externals.inputs marks.inputs modules u.prepared)
141+ ?: ?=(%| -.api) ~
142+ `iface.p.api
143+ ?~ found ~
144+ ~[[%todo u.found]]
145+ ?. ?=([* ~] resolved) ~["todo API evidence control did not check"]
146+ ?: (page-main-closed:jibb u.parts defs.p.parsed values.checked `iface.u.prepared resolved missing)
147+ ~["unknown request evidence passed data-page checking"]
148+ =/ store=(list tape)
149+ (page-main-negative 'store path' /ui/stores/about/jibb about externals.inputs marks.inputs modules u.prepared)
150+ ?^ store store
151+ (zing (turn negatives |=(c=[label=@t src=@t] (page-main-negative label.c /ui/index/jibb src.c externals.inputs marks.inputs modules u.prepared))))
152+::
153+++ test-page-main
154+ ^- tang
155+ =/ negatives=(list [label=@t src=@t])
156+ :~ ['wrong static message' (text wrong-static)]
157+ ['arbitrary main' (text arbitrary-main)]
158+ ['page alias' (text page-alias)]
159+ ['indirect load' (text indirect-load)]
160+ ['pure load' (text pure-load)]
161+ ['hand-built load' (text hand-built-load)]
162+ ['typed load' (text typed-load)]
163+ ['diff load' (text diff-load)]
164+ ==
165+ =/ failures=(list tape)
166+ (page-main-errors prelude internal (text sur) (text action) (text todos) (text update) (text item) (text todo-api) (text todo-island) (text blog-sur) (text posts) (text blog-api) (text card) (text about) (text blog-index) (text index) (text ctor-page) negatives)
167+ (turn failures |=(reason=tape leaf+reason))
168+--
tests/type-page-main/about.txtnew file+3
@@ -0,0 +1,3 @@
1+source: my-app/ui/about.jibb, my-app/ui/blog/index.jibb and my-app/ui/index.jibb; their fixtures and real API/UI/reader sources must remain byte-identical.
2+tests: Shared eval and ship full checks for static and direct data pages, exact closed main types, a direct-constructor reply, structural downstream imports, and rejection of wrong/arbitrary mains, page aliases, indirect, pure, hand-built, typed and diff loads, missing evidence and store paths. No rendering or runtime request execution is claimed.
3+expect: shared eval and ship guards
tests/type-page-main/arbitrary-main.txtnew file+1
@@ -0,0 +1 @@
1+++ main 42
tests/type-page-main/blog-api.txtnew file+5
@@ -0,0 +1,5 @@
1+:: Fixture for the documented examples; not supplied by the design doc.
2+/% %posts
3+
4++$ paths
5+ [%posts /posts posts]
tests/type-page-main/blog-index.txtnew file+9
@@ -0,0 +1,9 @@
1+/- blog
2+/= api /ui/api/blog
3+/= card /ui/components/card
4+
5+++ main (page load view)
6+++ load |=(~ (posts.api same))
7+
8+++ view |= ps=(list post.blog)
9+ column ~[gap+%s2] (turn ps card.card)
tests/type-page-main/blog-sur.txtnew file+6
@@ -0,0 +1,6 @@
1+:: Fixture for the documented examples; not supplied by the design doc.
2+|%
3++$ title @t
4++$ post [title=title slug=@ta]
5++$ chain $@(~ [value=@ud next=chain])
6+--
tests/type-page-main/card.txtnew file+5
@@ -0,0 +1,5 @@
1+:: Fixture for the documented examples; not supplied by the design doc.
2+/- blog
3+
4+++ card |= p=post.blog
5+ text title.p
tests/type-page-main/ctor-page.txtnew file+10
@@ -0,0 +1,10 @@
1+/= todo /ui/api/todo
2+
3++$ msg
4+ [%got todos=(list item.todo)]
5+++ main (page load view)
6+++ load |= ~
7+ ^- (ask msg) (todos.todo |=%got)
8+
9+++ view |= m=msg
10+ text "Todos loaded"
tests/type-page-main/diff-load.txtnew file+11
@@ -0,0 +1,11 @@
1+/= todo /ui/api/todo
2+/= todo-list /ui/todo-list
3+
4+++ main (page load view)
5+++ fold |= [d=[%todo-update todo-update.todo] s=todos.todo] s
6+++ load |=(~ (changes.todo same fold))
7+
8+++ view |= ts=(list item.todo)
9+ column ~[pad+%s3] ~[
10+ heading 1 "My ship"
11+ island main.todo-list ts
tests/type-page-main/hand-built-load.txtnew file+14
@@ -0,0 +1,14 @@
1+/= todo /ui/api/todo
2+/= todo-list /ui/todo-list
3+
4+++ main (page load view)
5+++ load |=(~
6+ ^- (ask (list item.todo))
7+ [%agent api="/ui/api/todo" case=%todos agent=%todo path=~[%todos] scry=~ receive=receive])
8+++ receive |= input=[mark=@tas state=(unit *) fact=*]
9+ ^- [state=* message=(list item.todo)] [state=0 message=~]
10+
11+++ view |= ts=(list item.todo)
12+ column ~[pad+%s3] ~[
13+ heading 1 "My ship"
14+ island main.todo-list ts
tests/type-page-main/index.txtnew file+10
@@ -0,0 +1,10 @@
1+/= todo /ui/api/todo
2+/= todo-list /ui/todo-list
3+
4+++ main (page load view)
5+++ load |=(~ (todos.todo same))
6+
7+++ view |= ts=(list item.todo)
8+ column ~[pad+%s3] ~[
9+ heading 1 "My ship"
10+ island main.todo-list ts
tests/type-page-main/indirect-load.txtnew file+11
@@ -0,0 +1,11 @@
1+/= todo /ui/api/todo
2+/= todo-list /ui/todo-list
3+
4+++ main (page load view)
5+++ request |=(~ (todos.todo same))
6+++ load |=(~ (request ~))
7+
8+++ view |= ts=(list item.todo)
9+ column ~[pad+%s3] ~[
10+ heading 1 "My ship"
11+ island main.todo-list ts
tests/type-page-main/page-alias.txtnew file+11
@@ -0,0 +1,11 @@
1+/= todo /ui/api/todo
2+/= todo-list /ui/todo-list
3+
4+++ make-page page
5+++ main (make-page load view)
6+++ load |=(~ (todos.todo same))
7+
8+++ view |= ts=(list item.todo)
9+ column ~[pad+%s3] ~[
10+ heading 1 "My ship"
11+ island main.todo-list ts
tests/type-page-main/posts-mark.txtnew file+6
@@ -0,0 +1,6 @@
1+:: Fixture for the documented examples; not supplied by the design doc.
2+|_ a=(list post:blog)
3+++ grab |% ++ noun (list post:blog) --
4+++ grow |% ++ noun a --
5+++ grad %noun
6+--
tests/type-page-main/pure-load.txtnew file+10
@@ -0,0 +1,10 @@
1+/= todo /ui/api/todo
2+/= todo-list /ui/todo-list
3+
4+++ main (page load view)
5+++ load |=(~ (pure ~))
6+
7+++ view |= ts=(list item.todo)
8+ column ~[pad+%s3] ~[
9+ heading 1 "My ship"
10+ island main.todo-list ts
tests/type-page-main/static.txtnew file+7
@@ -0,0 +1,7 @@
1+++ main view
2+
3+++ view
4+ column ~[width+[%fill 1] pad+%s3 gap+%s2] ~[
5+ heading 1 "About this ship"
6+ para ~[(text "Hosted on Urbit. This page ships no JavaScript.")]
7+ el ~[tone+%accent] (link %/blog "Read the blog")
tests/type-page-main/todo-api.txtnew file+13
@@ -0,0 +1,13 @@
1+:: /ui/api/todo.jibb
2+/- todo
3+/% %todo-action
4+/% %todos
5+/% %todo-update
6+/% %todo-item
7+
8++$ item item.todo
9++$ poke todo-action
10++$ paths
11+ [%todos /todos todos]
12+ [%changes /changes (facts todos todo-update)]
13+ [%todo-by-id /todo/[id=@ud] todo-item]
tests/type-page-main/todo-island.txtnew file+40
@@ -0,0 +1,40 @@
1+/= todo /ui/api/todo
2+
3+++ main (program init update view subs)
4+
5++$ model [todos=(list item.todo) draft=@t error=(unit @t)]
6++$ msg
7+ [%typed text=@t]
8+ %submit
9+ [%toggled id=@ud]
10+ [%got todos=(list item.todo)]
11+ [%acked err=(unit @t)]
12+
13+++ init |= ts=(list item.todo)
14+ [ts "" ~]
15+
16+++ update |= [=msg m=model]
17+ ?- msg
18+ [%typed t]
19+ ?- (gth (lent (tuba (trip t))) 80)
20+ %.y [m(draft=t error=`"Too long") ~]
21+ %.n [m(draft=t error=~) ~]
22+ %submit [m(draft="") ~[[%poke (todo-action.todo [%add draft.m]) |=%acked]]]
23+ [%toggled id] [m ~[[%poke (todo-action.todo [%toggle id]) |=%acked]]]
24+ [%got ts] [m(todos=ts) ~]
25+ [%acked err] [m(error=err) ~]
26+
27+++ subs |= m=model
28+ ~[[%watch (todos.todo |=%got)]]
29+
30+++ view |= m=model
31+ column ~[gap+%s2] ~[
32+ heading 2 "Todos"
33+ each (turn todos.m row-of)
34+ row ~[gap+%s1] ~[
35+ el ~[width+[%fill 1]] (field "New todo" draft.m |=%typed)
36+ button "Add" %submit
37+ el ~[tone+%danger] (text (fall error.m ""))
38+
39+++ row-of |= t=item.todo
40+ [id.t (toggle done.t text.t [%toggled id.t])]
tests/type-page-main/typed-load.txtnew file+8
@@ -0,0 +1,8 @@
1+/= todo /ui/api/todo
2+/= todo-list /ui/todo-list
3+
4+++ main (page load view)
5+++ load |=(~ (todo-by-id.todo 1 |=%one))
6+
7+++ view |= item=todo-item.todo
8+ text "One todo"
tests/type-page-main/wrong-static.txtnew file+1
@@ -0,0 +1 @@
1+++ main (button "Go" %go)
tools/testdata/ship-selftest+9
@@ -324,6 +324,7 @@ def generator_checks(tmp):
324 'tests/lib/jibb-closed-gate-guards.hoon',
324 'tests/lib/jibb-closed-gate-guards.hoon',
325 'tests/lib/jibb-direct-gate-guards.hoon',
325 'tests/lib/jibb-direct-gate-guards.hoon',
326 'tests/lib/jibb-direct-gate-source.hoon',
326 'tests/lib/jibb-direct-gate-source.hoon',
327+ 'tests/lib/jibb-page-main.hoon',
327 'tests/lib/jibb-program-main.hoon',
328 'tests/lib/jibb-program-main.hoon',
328 'tests/lib/jibb-program-init.hoon',
329 'tests/lib/jibb-program-init.hoon',
329 'tests/lib/jibb-program-update.hoon',
330 'tests/lib/jibb-program-update.hoon',
@@ -400,6 +401,14 @@ def generator_checks(tmp):
400 check('direct gate source and golden are assembled',
401 check('direct gate source and golden are assembled',
401 {'tests/type-direct-gate/source.txt', 'tests/type-direct-gate/expected.txt'}
402 {'tests/type-direct-gate/source.txt', 'tests/type-direct-gate/expected.txt'}
402 <= set(normal.files))
403 <= set(normal.files))
404+ check('static and data page sources and shared fixture are assembled',
405+ {'tests/lib/jibb-page-main.hoon', 'tests/type-page-main/about.txt',
406+ *(f'tests/type-page-main/{name}.txt' for name in (
407+ 'static', 'blog-index', 'index', 'ctor-page', 'blog-api', 'card', 'blog-sur',
408+ 'posts-mark', 'todo-api', 'todo-island', 'wrong-static',
409+ 'arbitrary-main', 'page-alias', 'indirect-load', 'pure-load',
410+ 'hand-built-load', 'typed-load', 'diff-load'))}
411+ <= set(normal.files))
403 check('program main sources and shared fixture are assembled',
412 check('program main sources and shared fixture are assembled',
404 {f'tests/type-program-main/{name}.txt' for name in (
413 {f'tests/type-program-main/{name}.txt' for name in (
405 'source', 'consumer', 'gate-model', 'gate-data', 'nonempty-subs',
414 'source', 'consumer', 'gate-model', 'gate-data', 'nonempty-subs',
tools/type-page-main-testnew file+86
@@ -0,0 +1,86 @@
1+#!/usr/bin/env python3
2+"""Check static and direct data pages through real readers under eval."""
3+from pathlib import Path
4+import argparse
5+import subprocess
6+import sys
7+import tempfile
8+
9+root = Path(__file__).resolve().parent.parent
10+parser = argparse.ArgumentParser(description=__doc__)
11+parser.add_argument('--lib', default=str(root / 'jibb-dev/lib/jibb.hoon'))
12+parser.add_argument('--timeout', type=float, default=600,
13+ help='seconds allowed for urbit eval (default: 600)')
14+args = parser.parse_args()
15+for fixture, actual in (
16+ ('tests/type-page-main/static.txt', 'my-app/ui/about.jibb'),
17+ ('tests/type-page-main/blog-index.txt', 'my-app/ui/blog/index.jibb'),
18+ ('tests/type-page-main/index.txt', 'my-app/ui/index.jibb'),
19+ ('tests/type-page-main/blog-api.txt', 'my-app/ui/api/blog.jibb'),
20+ ('tests/type-page-main/card.txt', 'my-app/ui/components/card.jibb'),
21+ ('tests/type-page-main/blog-sur.txt', 'my-app/sur/blog.hoon'),
22+ ('tests/type-page-main/todo-api.txt', 'my-app/ui/api/todo.jibb'),
23+ ('tests/type-page-main/todo-island.txt', 'my-app/ui/todo-list.jibb')):
24+ if (root / fixture).read_bytes() != (root / actual).read_bytes():
25+ sys.exit(f'{fixture} differs from {actual}')
26+# As with the todo readers, eval supplies the imported core in the subject;
27+# only the Ford import line is removed from the real mark source.
28+posts = (root / 'my-app/mar/posts.hoon').read_bytes().replace(b'/- blog\n', b'', 1)
29+if (root / 'tests/type-page-main/posts-mark.txt').read_bytes() != posts:
30+ sys.exit('posts reader fixture differs from my-app/mar/posts.hoon without /- blog')
31+reader = (root / 'jibb-dev/lib/jibb-import.hoon').read_text()
32+tests = (root / 'tests/lib/jibb-page-main.hoon').read_text()
33+tests = tests[tests.index('\n|%\n') + 1:]
34+fixtures = {
35+ 'sur': 'tests/type-api-paths/todo-sur.txt',
36+ 'action': 'tests/type-api-paths/todo-action.txt',
37+ 'todos': 'tests/type-api-paths/todos.txt',
38+ 'update': 'tests/type-api-paths/todo-update.txt',
39+ 'item': 'tests/type-api-paths/todo-item.txt',
40+ **{name: f'tests/type-page-main/{name}.txt' for name in (
41+ 'todo-api', 'todo-island', 'blog-sur', 'posts', 'blog-api',
42+ 'card', 'static', 'blog-index', 'index', 'ctor-page', 'wrong-static',
43+ 'arbitrary-main', 'page-alias', 'indirect-load', 'pure-load',
44+ 'hand-built-load', 'typed-load', 'diff-load')},
45+}
46+fixtures['posts'] = 'tests/type-page-main/posts-mark.txt'
47+# The Hoon face for the static source is about; its metadata remains about.txt.
48+cords = dict(fixtures)
49+cords['about'] = cords.pop('static')
50+prefix = ('=/ jibb .\n=/ jibb-import\n' + reader + '\n' +
51+ ''.join('=/ ' + name + '=wain (to-wain:format ' + name + '-text)\n'
52+ for name in cords) + '=/ suite\n' + tests)
53+negative_names = ('wrong-static', 'arbitrary-main', 'page-alias',
54+ 'indirect-load', 'pure-load', 'hand-built-load',
55+ 'typed-load', 'diff-load')
56+negative_labels = ('wrong static message', 'arbitrary main', 'page alias',
57+ 'indirect load', 'pure load', 'hand-built load',
58+ 'typed load', 'diff load')
59+negatives = ''.join(f" ['{label}' (text:suite {name})]\n"
60+ for label, name in zip(negative_labels, negative_names))
61+ordered = ('sur', 'action', 'todos', 'update', 'item', 'todo-api',
62+ 'todo-island', 'blog-sur', 'posts', 'blog-api', 'card',
63+ 'about', 'blog-index', 'index', 'ctor-page')
64+arguments = ' '.join(f'(text:suite {name})' for name in ordered)
65+expression = (prefix + '\n=/ negatives=(list [label=@t src=@t])\n :~\n' +
66+ negatives + ' ==\n' +
67+ '=/ failures=(list tape) (page-main-errors:suite prelude internal ' +
68+ arguments + ' negatives)\n^- @t\n'
69+ "?~ failures 'static and direct data-page checks passed'\n"
70+ '(of-wain:format (turn failures crip))\n')
71+with tempfile.TemporaryDirectory(prefix='jibb-page-main-') as tmp:
72+ path = Path(tmp) / 'page-main.hoon'
73+ path.write_text(expression)
74+ command = [str(root / 'tools/hoon'), '--lib', args.lib, '--raw', '-f', str(path),
75+ '--timeout', str(args.timeout),
76+ '--cord', 'prelude=' + str(root / 'jibb-dev/lib/jibb/prelude.jibb'),
77+ '--cord', 'internal=' + str(root / 'jibb-dev/lib/jibb/internal.jibb')]
78+ for name, filename in cords.items():
79+ command += ['--cord', name + '-text=' + str(root / filename)]
80+ result = subprocess.run(command, stdout=subprocess.PIPE)
81+if result.returncode:
82+ sys.exit(result.returncode)
83+if result.stdout != b'static and direct data-page checks passed\n':
84+ sys.stderr.buffer.write(result.stdout)
85+ sys.exit(1)
86+print('byte-identical static/data pages, exact types, imports and negatives passed')