Skip to main content

Module project_model

Module project_model 

Source
Expand description

Project-wide orchestration: discovery → parse → group → resolve, shared between bynk-emit’s run_checks (both Mode::Build and Mode::Analyse) and this crate’s own crate::analysis::analyse_project.

P4.1 (#1115), second scope finding on the tracking issue: this pipeline — phase_discovery through assemble_unit_info, plus the per-unit symbol composition (compose_unit_symbols/merge_consumed_exports/ collect_unit_methods) — used to live only in bynk-emit/src/project.rs, inline in run_checks. A literal no-indirection bynk-check-side analysis entry point needs the identical sequence, so rather than write a second, independently-maintained copy (the mistake this whole design track’s extract, don't duplicate principle exists to prevent — see lower_field_default_wire, build_capability_op_info for the same move made earlier in this track), it moved here. bynk-emit’s run_checks becomes a caller of these functions instead of owning the logic, the same way P4.0 turned project.rs into a caller of bynk-project.

What stayed in bynk-emit (not shared, because only the Mode::Build path needs it, or because it’s genuinely emission-shaped): the Mode::Build bail gate and everything from emission onward (EmitUnitCtx, emit_unit, collect_history_target_agents). The whole-project messages/locale- ambiguity/event-subscription checks (P5.0/P5.1), the function-type- boundary check (P5.2, phase_function_type_boundaries), and schema-registry reconciliation/platform-lock enforcement (P5.3, crate::schema_registry::reconcile/phase_platform_lock) have since moved here too — the P5.2 move closed phase_group’s optional boundary-check hook, which used to be the only way run_checks and the new entry point could reach it without duplicating the diagnostic-ordering logic (see analysis.rs for the residual-gap accounting that remains).

Structs§

AdapterBinding
v0.17: a resolved adapter binding — the user-authored .binding.ts module that supplies an adapter’s external provider symbols. Copied verbatim into the output beside the adapter’s emitted interface module so that tsc checks the implements contract and compose can import the symbols.
ErrorSink
Collection-point error sink (ADR 0052). Helpers keep their plain &mut Vec<CompileError> signatures; call sites attribute via extend_for with the file in scope at that point.
UnitInfo
v0.29.4: the per-unit facets that the producer phases build as nine parallel HashMap<String, _>s, all keyed on unit name. Assembling one record per unit makes the “all these maps share one keyset” invariant structural: a single lookup yields every facet as a field, so the per-column .unwrap()s on the shared keyset disappear. Fields are total — exports/aliases/flattened default to an empty map for a unit with no entry, reproducing the old .unwrap_or(empty) read semantics without the dance.

Enums§

BuildTarget
The build target. Determines how cross-context calls and per-context modules are emitted (v0.8). Bundle mode is the default — all contexts emit into one TypeScript bundle and cross-context calls are direct function invocations. Workers mode produces per-context Cloudflare Worker bundles that communicate via Service Bindings.

Constants§

FIRSTPARTY_ID_BASE
FIRSTPARTY_ID_BLOCK
A memoized parse of one first-party synthetic source, keyed by the call-site’s own cache static — each of phase_parse’s 7 injection sites below passes a distinct one. Finding #55/#65: the source text is a fixed include_str! constant, so its parse is a pure function of that constant and only needs computing once per process, not once per compile/analyse round. The gating below (consumes_bynk, uses_map, etc.) is unaffected — it still runs fresh for every project from that project’s own parsed uses/consumes; only the parse result being gated is cached. T3.4 (R2.4): each first-party synthetic unit reserves its own 1M-wide ExprId block, spaced far above anything a real project’s own file count could ever reach — see firstparty_parsed’s doc comment for why a fixed reservation, not a threaded counter, is the right shape here.

Functions§

assemble_unit_info
v0.29.4: fold the nine parallel per-unit maps into one HashMap<String, UnitInfo>. Assembly is driven by the groups keyset (the authority), so every group yields exactly one record. Facets that are genuinely optional in the producer maps (exports/aliases/flattened, and file_index for a unit with no declarations) default to empty — reproducing the old .unwrap_or(empty) read semantics as a total field.
check_discovered_files
The checks every tree’s file list must pass regardless of where it came from — a real disk walk (phase_discovery) or a caller-supplied discovered/CompileOptions.sources list (#1077/#1081 review). An empty project (bynk.project.no_sources) signals a bail via Err(()); a file/directory name conflict is a non-fatal diagnostic.
check_function_type_boundary_items
Item-level body of the boundary confinement, shared with the single-file (legacy) compile path in bynk-emit’s lib.rs. Relocated alongside phase_function_type_boundaries (P5.2).
collect_type_decls
v0.174 (#592): a name -> TypeDecl table over a set of items, for the recursive-generic boundary walk. Relocated alongside phase_function_type_boundaries (P5.2) — public since bynk-emit’s single-file compile path (lib.rs) also needs it, across the crate boundary this relocation now draws.
collect_unit_methods
Phase 8c: collect every method authored anywhere in one unit, keyed by its attached type’s name — so a type’s methods surface in the file that declares the type even when the method lives in a sibling file. The collection loop has no continues, so it lifts out whole.
compose_unit_symbols
Phase 8a: compose one unit’s symbol space — its local table plus a one-level uses mixin (commons identity preserved). Returns the combined type/fn/method tables and the imported_from provenance maps; the mixin loop has no continues, so it lifts out whole.
firstparty_parsed
handler_cross_caps
v0.15: the cross-context capabilities a context’s handlers reference, as deps_key → consumed_context. Shared with bynk-emit, not duplicated — see resolve_consume_prefix’s doc.
inject_service_defaults
Inject a single service’s by/given defaults into its handlers. A handler that names its own by (or given) overrides the default outright — the default fills only an absent clause, never merges. A service with no default is left untouched (byte-for-byte the pre-v0.155 behaviour).
merge_consumed_exports
Phase 8b: merge one context’s consumes exports into the composed symbol space, recording visibility metadata in the returned consumed_types. The per-export continues (missing decl, name conflict) stay internal to the loop, which lifts out whole; name conflicts are pushed into errors and the caller’s group_error_baseline guard reacts to them after this returns.
normalize_service_defaults
phase_consumes_aliases
Phases 5b’/5b’‘: collect each context’s consumes aliases (alias → consumed-context name), reporting alias-vs-alias conflicts (5b’), then report any alias that clashes with a locally-declared type/fn/capability/service/agent (5b’’). Returns the per-context alias maps; diagnostics go into errors.
phase_detect_consumes_cycles
Phase 5c: detect consumes cycles. #696: record each consumes-clause site (file + span) keyed by (consumer, target) so a detected cycle anchors on the exact clause that forms the closing edge — a real span in a real file — and renders with source context. Synthetic units are left out so their (snapshot-less) files never claim a diagnostic.
phase_discovery
Phase 1: discover the .bynk files under the source (and, in split mode, the tests) root by walking the filesystem. Pushes any discovery error into errors and signals a pipeline bail via Err(()) (the caller terminates with finish); otherwise returns the discovered (src_files, tests_files).
phase_event_subscriptions
Events track, slice 0 (spine #936): a from Events(E) subscription must name a real, declared event — owned either by this context or by a context it consumes (mirroring discover_event_subscribers’s own ownership resolution, project.rs, which silently drops an unresolvable subscription rather than diagnosing it). Runs at the project-wide phase (needs unit_tables + unit_consumes together, unlike the local, per- context check_service_protocols), alongside the other cross-unit checks that need the same two maps.
phase_file_index
Phase 7: build each production unit’s file-declaration index (which file in the unit declares which name), for cross-file lookups in the back half.
phase_function_type_boundaries
v0.20a: apply the function-type boundary confinement to every serialisable or boundary-crossing position in a file’s items: record fields and sum payloads (types can cross contexts and persist), service/agent handler signatures (the Workers wire), capability operation signatures (kept out in v0.20a — see ADR 0030), agent state fields, and agent keys. Free fn signatures are deliberately NOT walked — they are the non-boundary home of function types.
phase_group
Phase 3: group the parsed units by qualified name (production units, unit tests, and integration suites tracked separately), run the per-directory and path/name consistency checks, enforce the reserved bynk namespace and the adapter binding rules, resolve each adapter’s binding module, and fold the adapters’ pinned npm dependencies. Pushes diagnostics into errors and returns the production groups/kinds, the test/integration groups, the resolved adapter_bindings, and the collected npm_deps.
phase_locale_bundle_ambiguity
Locale capability track, slice 2 (#882): a context whose direct uses reaches two or more message-bundle commons has no principled single answer for what Locale.current() should negotiate against — but this is only worth diagnosing when the context actually consumes bynk { Locale } at all; a context with 2+ bundles that never touches Locale has nothing ambiguous to resolve.
phase_messages_bundles
message-bundles slice 1 (#859): messages-block legality, @reference cardinality, within-block duplicate codes, and the uses bynk.locale dependency. Runs here (not in phase_group) because it needs unit_uses, resolved just above.
phase_parse
Phase 2: parse every discovered file into a ParsedFile, recording each file’s source text into snapshots and any parse errors into errors. Then inject the first-party synthetic units (the bynk/bynk.cloudflare adapters and the bynk.{list,map,string} commons) that the project consumes/uses. Returns the parsed units plus whether the bynk and bynk.cloudflare adapters were injected; signals a pipeline bail via Err(()) when parsing produced errors and yielded no units at all.
phase_platform_lock
v0.19 (decisions 0017/0024): enforce the platform lock per deployment unit — each context under --target workers, the whole program under bundle (co-location shares the lock).
phase_resolve_consumes
Phase 5b: resolve each unit’s consumes clauses (target exists, is a context or adapter, not self-referential, obeys the adapter selection rules), and for the braced consumes U { Cap, … } form validate and record the flattened capabilities. Returns unit → consumed targets and unit → flattened-cap → owning unit; diagnostics go into errors and clause-position references into refs.
phase_resolve_uses
Phase 5: resolve each unit’s uses clauses, checking the target exists, is a commons, and is not self-referential. Returns unit → deduplicated list of used commons; diagnostics go into errors.
phase_secrets_computed_name
v0.173 (ADR 0196 D1), P5.5 (design/tracks/semantics-in-the-checker.md §6, §9): warn where a bynk.Secrets read names its secret with a computed expression. Non-failing — the program is correct, bynk deploy simply cannot see the name — walked per file rather than per unit, since a merged UnitTable has thrown away which file a call site lives in and ErrorSink::extend_for attributes a diagnostic to a path.
phase_symbol_tables
Phase 4: build each production unit’s combined symbol table from its files, pushing any table-construction errors into errors.
phase_uses_name_conflicts
Phase 6: for each unit, detect when two uses-imported commons declare the same (non-shadowed) type or function name — an unrenamable conflict at the use site. Diagnostics go into errors.
phase_validate_capability_exports
Phase 6b’: validate each context/adapter’s exports capability { … } clauses (v0.15 §4.1) — every name must be a capability the unit declares and provides, with no duplicate exports. Diagnostics go into errors and export references into refs.
phase_validate_providers
Phase 6c: validate that every (non-external) provider matches its capability exactly — each capability op has a provider op, and every provider op has a matching capability op with the same parameter and return types. Diagnostics go into errors.
phase_validate_type_exports
Phase 6b: validate each context/adapter’s exports opaque/transparent { … } clauses — every name must be a locally-declared type, with no duplicates within a clause or conflicting visibilities across clauses. Returns unit → (type → visibility); diagnostics go into errors and export references into refs.
resolve_consume_prefix
v0.15’s cross-context capability resolution, relocated alongside phase_platform_lock (P5.3): resolve a given/handler capability prefix (ctx.Cap) against a context’s own consumes/alias tables. Pure — no codegen, no bynk-emit dependency of its own — so unlike collect_given_closure this one is shared rather than duplicated: bynk-emit/src/project.rs’s own copy of this function (and of handler_cross_caps) was deleted in review (#1133) and every one of its call sites repointed here — bynk-emit already depends on bynk-check, so there was no dependency direction to route around, and keeping two copies only bought two things that could drift out of sync for no reason.
tree_root_for
The include tree that discovered pf, found by matching its absolute path against each tree’s root — not trees[0] unconditionally, since R3.9 (#1113) lets a file live under any include tree, not just the first. Falls back to trees[0] for a pf with no abs_path (unreachable for a real adapter: only synthetic units, which never declare binding, go without one) or if it somehow matches none. The longest matching root wins, in case one include tree is nested inside another.