Skip to content

Commit 3afadbd

Browse files
committed
feat(spec): declare publicPicker on FormFieldSchema (#7467)
The REST public-lookup route (GET /forms/:slug/lookup/:field) has always gated the anonymous picker on a publicPicker block that no schema declared: FormFieldSchema is strict (ADR-0089 D3a), so every authoring path refused a form carrying one and the capability was unreachable — ADR-0049's 'declared ≠ enforced' in the mirror direction. Per the maintainer ruling on the card (declare, option 1), FormFieldSchema now carries an optional publicPicker block mirroring exactly the route's four reads: - displayFields (≤5, the route's projection cap; omitted → ['name']) - maxResults (int 1..50, encoding the route's hard ceiling; default 20) - filter (ViewFilterRuleSchema[], the dialect the route composes) - object (referenced-object override) An unknown subkey stays a loud unrecognized_keys error; picker.sort — a fifth route read outside the ruling's enumeration — stays deliberately undeclared and pinned as such (follow-up filed from #7467). Also: ADR-0049 liveness ledger (view.json sections row + viewschema audit), the owner_id pin rebuilt through the real schema, a stored-row e2e proving a spec-valid form with a picker gets a real lookup answer, docs for the lookup route, regen artifacts, minor changeset. Closes #7467 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CnMk7vfrt2zW7xvwLS3JDf
1 parent afdc6ea commit 3afadbd

15 files changed

Lines changed: 647 additions & 18 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
"@objectstack/spec": minor
3+
---
4+
5+
feat(spec): declare `publicPicker` on `FormFieldSchema` — the REST public-lookup opt-in becomes authorable (#7467)
6+
7+
`GET /forms/:slug/lookup/:field` has always gated the anonymous public-form
8+
picker on an opt-in `publicPicker` block on the field's form declaration —
9+
without one it answers `403 LOOKUP_NOT_PUBLIC`, loud by design (#3022). But
10+
the key was declared in **no** schema: `FormFieldSchema` is strict (ADR-0089
11+
D3a), so `ViewMetadataSchema` refused any form carrying a picker with
12+
`unrecognized_keys`, `saveMetaItem` turned that into a 422, and code-authored
13+
forms hit the same wall at `FormViewSchema.parse`. The route, its projection,
14+
its result cap, its pre-filter and its object override were live code that no
15+
spec-valid form could turn on — ADR-0049's "declared ≠ enforced" in the mirror
16+
direction: **enforced, never declarable**.
17+
18+
Per the maintainer's ruling on #7467 (declare, option 1 of the card's fork —
19+
the retirement direction is closed), `FormFieldSchema` now carries an optional
20+
`publicPicker` block, `FormFieldPublicPickerSchema`, mirroring **exactly** the
21+
four reads the route performs and nothing wider — this block opens an
22+
unauthenticated search surface, so the schema deliberately admits no option
23+
the route does not enforce, and the route's hard bounds are encoded rather
24+
than left to silent request-time adjustment:
25+
26+
- `displayFields` — the projection (and `contains`-search target, first
27+
entry); at most 5, because the route never projects more; omitted → `['name']`.
28+
- `maxResults` — integer 1–50, encoding the route's hard ceiling of 50
29+
(default 20); `51`, `0`, negatives and fractions are authoring-time parse
30+
errors instead of silently clamped values.
31+
- `filter``ViewFilterRuleSchema[]`, the same rule dialect the route
32+
composes with its own search predicate (shape coupling per #6227 included).
33+
- `object` — the referenced-object override the route prefers before falling
34+
back to the field definition.
35+
36+
An unknown subkey is still a loud `unrecognized_keys` error (ADR-0089 D3a),
37+
and `publicPicker.sort` — a fifth read the route performs that the ruling's
38+
enumeration does not include — stays deliberately undeclared and pinned as
39+
such (follow-up filed from #7467).
40+
41+
**minor**, not patch: this adds a new authorable property to the public
42+
acceptance surface — metadata that yesterday was a 422 parses today
43+
(precedent: #7387 per #3405/#5583). Nothing previously accepted changes shape
44+
or is refused; the REST route is untouched.

content/docs/references/index.mdx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Protocol Reference
3-
description: Every schema published by @objectstack/spec — 1577 schemas across 14 protocol modules
3+
description: Every schema published by @objectstack/spec — 1578 schemas across 14 protocol modules
44
---
55

66
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
@@ -32,8 +32,8 @@ counts are sums of the rows they head. Regenerate with
3232
| [Shared Protocol](/docs/references/shared) | 8 | 31 | Primitives used across every protocol — identifiers, HTTP, expressions, error maps, enums. |
3333
| [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. |
3434
| [System Protocol](/docs/references/system) | 37 | 292 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
35-
| [UI Protocol](/docs/references/ui) | 16 | 147 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
36-
| **Total** | **200** | **1577** | 14 protocol modules |
35+
| [UI Protocol](/docs/references/ui) | 16 | 148 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
36+
| **Total** | **200** | **1578** | 14 protocol modules |
3737

3838
---
3939

@@ -366,7 +366,7 @@ The runtime environment — logging, jobs, cache, metrics, notifications, i18n a
366366

367367
## UI Protocol
368368

369-
**Source:** `packages/spec/src/ui/` · **Import:** `@objectstack/spec/ui` · **16 pages, 147 schemas**
369+
**Source:** `packages/spec/src/ui/` · **Import:** `@objectstack/spec/ui` · **16 pages, 148 schemas**
370370

371371
Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer.
372372

@@ -387,7 +387,7 @@ Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI lay
387387
| [`responsive.zod.ts`](/docs/references/ui/responsive) | `BreakpointColumnMap`, `BreakpointName`, `BreakpointOrderMap`, `ResponsiveConfig`, `ResponsiveStyles`, `StyleMap` |
388388
| [`sharing.zod.ts`](/docs/references/ui/sharing) | `SharingConfig` |
389389
| [`theme.zod.ts`](/docs/references/ui/theme) | `BorderRadius`, `ColorPalette`, `Shadow`, `Theme`, `ThemeMode`, `Typography` |
390-
| [`view.zod.ts`](/docs/references/ui/view) | `AddRecordConfig`, `AppearanceConfig`, `CalendarConfig`, `ColumnPrefix`, `ColumnSummary`, `ColumnSummaryConfig`, `FormButtonConfig`, `FormField`, `FormSection`, `FormView`, `GalleryConfig`, `GanttConfig`, `GanttQuickFilter`, `GroupingConfig`, `GroupingField`, `HttpMethodSubset`, `HttpRequest`, `KanbanConfig`, `ListChartConfig`, `ListColumn`, `ListView`, `NavigationConfig`, `NavigationMode`, `ObjectListView`, `ObjectUserFilters`, `PaginationConfig`, `RowColorConfig`, `RowHeight`, `SelectionConfig`, `TimelineConfig`, `TreeConfig`, `UserActionsConfig`, `UserFilterField`, `UserFilters`, `View`, `ViewData`, `ViewFilterRule`, `ViewItem`, `ViewItemName`, `ViewItemWire`, `ViewKind`, `ViewScope`, `ViewSharing`, `ViewTab`, `VisualizationType` |
390+
| [`view.zod.ts`](/docs/references/ui/view) | `AddRecordConfig`, `AppearanceConfig`, `CalendarConfig`, `ColumnPrefix`, `ColumnSummary`, `ColumnSummaryConfig`, `FormButtonConfig`, `FormField`, `FormFieldPublicPicker`, `FormSection`, `FormView`, `GalleryConfig`, `GanttConfig`, `GanttQuickFilter`, `GroupingConfig`, `GroupingField`, `HttpMethodSubset`, `HttpRequest`, `KanbanConfig`, `ListChartConfig`, `ListColumn`, `ListView`, `NavigationConfig`, `NavigationMode`, `ObjectListView`, `ObjectUserFilters`, `PaginationConfig`, `RowColorConfig`, `RowHeight`, `SelectionConfig`, `TimelineConfig`, `TreeConfig`, `UserActionsConfig`, `UserFilterField`, `UserFilters`, `View`, `ViewData`, `ViewFilterRule`, `ViewItem`, `ViewItemName`, `ViewItemWire`, `ViewKind`, `ViewScope`, `ViewSharing`, `ViewTab`, `VisualizationType` |
391391

392392
---
393393

content/docs/references/ui/view.mdx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ Migrated to [shared/http.zod.ts](/docs/references/shared/http). Re-exported here
1515
## TypeScript Usage
1616

1717
```typescript
18-
import { AddRecordConfigSchema, AppearanceConfigSchema, CalendarConfigSchema, ColumnPrefixSchema, ColumnSummarySchema, ColumnSummaryConfigSchema, FormButtonConfigSchema, FormFieldSchema, FormSectionSchema, FormViewSchema, GalleryConfigSchema, GanttConfigSchema, GanttQuickFilterSchema, GroupingConfigSchema, GroupingFieldSchema, HttpMethodSubsetSchema, HttpRequestSchema, KanbanConfigSchema, ListChartConfigSchema, ListColumnSchema, ListViewSchema, NavigationConfigSchema, NavigationModeSchema, ObjectListViewSchema, ObjectUserFiltersSchema, PaginationConfigSchema, RowColorConfigSchema, RowHeightSchema, SelectionConfigSchema, TimelineConfigSchema, TreeConfigSchema, UserActionsConfigSchema, UserFilterFieldSchema, UserFiltersSchema, ViewSchema, ViewDataSchema, ViewFilterRuleSchema, ViewItemSchema, ViewItemNameSchema, ViewItemWireSchema, ViewKindSchema, ViewScopeSchema, ViewSharingSchema, ViewTabSchema, VisualizationTypeSchema } from '@objectstack/spec/ui';
19-
import type { AddRecordConfig, AppearanceConfig, CalendarConfig, ColumnPrefix, ColumnSummary, ColumnSummaryConfig, FormButtonConfig, FormField, FormSection, FormView, GalleryConfig, GanttConfig, GanttQuickFilter, GroupingConfig, HttpMethodSubset, HttpRequest, KanbanConfig, ListChartConfig, ListColumn, ListView, NavigationConfig, NavigationMode, PaginationConfig, RowColorConfig, RowHeight, SelectionConfig, TimelineConfig, TreeConfig, UserActionsConfig, UserFilterField, UserFilters, View, ViewData, ViewFilterRule, ViewItem, ViewItemName, ViewItemWire, ViewKind, ViewScope, ViewSharing, ViewTab, VisualizationType } from '@objectstack/spec/ui';
18+
import { AddRecordConfigSchema, AppearanceConfigSchema, CalendarConfigSchema, ColumnPrefixSchema, ColumnSummarySchema, ColumnSummaryConfigSchema, FormButtonConfigSchema, FormFieldSchema, FormFieldPublicPickerSchema, FormSectionSchema, FormViewSchema, GalleryConfigSchema, GanttConfigSchema, GanttQuickFilterSchema, GroupingConfigSchema, GroupingFieldSchema, HttpMethodSubsetSchema, HttpRequestSchema, KanbanConfigSchema, ListChartConfigSchema, ListColumnSchema, ListViewSchema, NavigationConfigSchema, NavigationModeSchema, ObjectListViewSchema, ObjectUserFiltersSchema, PaginationConfigSchema, RowColorConfigSchema, RowHeightSchema, SelectionConfigSchema, TimelineConfigSchema, TreeConfigSchema, UserActionsConfigSchema, UserFilterFieldSchema, UserFiltersSchema, ViewSchema, ViewDataSchema, ViewFilterRuleSchema, ViewItemSchema, ViewItemNameSchema, ViewItemWireSchema, ViewKindSchema, ViewScopeSchema, ViewSharingSchema, ViewTabSchema, VisualizationTypeSchema } from '@objectstack/spec/ui';
19+
import type { AddRecordConfig, AppearanceConfig, CalendarConfig, ColumnPrefix, ColumnSummary, ColumnSummaryConfig, FormButtonConfig, FormField, FormFieldPublicPicker, FormSection, FormView, GalleryConfig, GanttConfig, GanttQuickFilter, GroupingConfig, HttpMethodSubset, HttpRequest, KanbanConfig, ListChartConfig, ListColumn, ListView, NavigationConfig, NavigationMode, PaginationConfig, RowColorConfig, RowHeight, SelectionConfig, TimelineConfig, TreeConfig, UserActionsConfig, UserFilterField, UserFilters, View, ViewData, ViewFilterRule, ViewItem, ViewItemName, ViewItemWire, ViewKind, ViewScope, ViewSharing, ViewTab, VisualizationType } from '@objectstack/spec/ui';
2020

2121
// Validate data
2222
const result = AddRecordConfigSchema.parse(data);
@@ -139,6 +139,7 @@ Column footer summary configuration
139139
| **type** | `Enum<'text' \| 'textarea' \| 'email' \| 'url' \| 'phone' \| 'password' \| 'secret' \| 'markdown' \| 'html' \| 'richtext' \| 'number' \| 'currency' \| 'percent' \| 'date' \| … +35 more>` | optional | Field type (auto-infers widget if omitted) |
140140
| **options** | `{ label: string; value: string; color?: string; default?: boolean; … }[]` | optional | Options for select/multiselect/radio/checkboxes fields |
141141
| **reference** | `string` | optional | Target object name for lookup/master_detail fields |
142+
| **publicPicker** | `{ displayFields?: string[]; maxResults?: integer; filter?: object[]; object?: string }` | optional | Opt this field into the anonymous public-form lookup picker (GET /forms/:slug/lookup/:field). Without it the route answers 403 LOOKUP_NOT_PUBLIC and the field is stripped from the rendered public form. |
142143
| **maxLength** | `number` | optional | Maximum character length (for text/textarea/email/url/phone) |
143144
| **minLength** | `number` | optional | Minimum character length |
144145
| **min** | `number` | optional | Minimum value (for number/currency/percent/slider) |
@@ -217,6 +218,22 @@ Column footer summary configuration
217218
* `vector`
218219

219220

221+
---
222+
223+
## FormFieldPublicPicker
224+
225+
Public-lookup opt-in: enables GET /forms/:slug/lookup/:field for this field on an anonymous public form (without it the route answers 403 LOOKUP_NOT_PUBLIC).
226+
227+
### Properties
228+
229+
| Property | Type | Required | Description |
230+
| :--- | :--- | :--- | :--- |
231+
| **displayFields** | `string[]` | optional | Fields projected into each picker result (with `id`); the visitor's search matches `contains` on the first entry. At most 5 (the route projects no more); omitted → ['name']. |
232+
| **maxResults** | `integer` | optional | Maximum rows a lookup returns (default 20, hard ceiling 50 — the route clamps; anonymous visitors cannot paginate past it). |
233+
| **filter** | `{ field: string; operator: Enum<'equals' \| 'not_equals' \| 'contains' \| 'not_contains' \| 'starts_with' \| … +14 more>; value?: string \| number \| boolean \| null \| (string \| number)[] }[]` | optional | Static pre-filter rows ANDed ahead of the visitor's search (e.g. only active records are searchable). Same `{ field, operator, value }` dialect as list-view filters. |
234+
| **object** | `string` | optional | Referenced-object override for the picker search; omitted → resolved from the field definition (`referenceTo`). |
235+
236+
220237
---
221238

222239
## FormSection

content/docs/ui/forms.mdx

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,10 +232,57 @@ Errors:
232232

233233
The companion `GET /api/v1/forms/:slug` route returns `500 FORM_RESOLVE_FAILED` if form resolution itself throws.
234234

235+
### `GET /api/v1/forms/:slug/lookup/:field` — the public picker
236+
237+
Lookup, master-detail and `user` fields are **stripped from a public form by
238+
default** — surfacing one to anonymous visitors means exposing a record search
239+
to the internet, so it is opt-in per field, like Airtable's "Allow linking to
240+
existing records" toggle. The opt-in is a `publicPicker` block on the field's
241+
entry in `sections[].fields[]` (declarable since #7467):
242+
243+
```ts
244+
sections: [{
245+
label: 'About you',
246+
fields: [
247+
'company',
248+
{ field: 'owner', publicPicker: { displayFields: ['name'], maxResults: 10 } },
249+
],
250+
}]
251+
```
252+
253+
| `publicPicker` key | Meaning |
254+
|---|---|
255+
| `displayFields` | Fields projected into each result row (plus `id`); the visitor's `q` is `contains`-matched against the **first** entry. At most 5; omitted → `['name']`. |
256+
| `maxResults` | Rows per request, integer 1–50 (default 20). 50 is a hard server ceiling; there is **no pagination** on this surface (`offset` is pinned to 0), so a leaked endpoint cannot enumerate the table. |
257+
| `filter` | Static pre-filter rows (same `{ field, operator, value }` dialect as list-view filters), ANDed ahead of the visitor's search. |
258+
| `object` | The object to search; omit to let the server resolve it from the field definition. |
259+
260+
The block admits exactly what the route enforces — an unknown subkey, a 6th
261+
display field, or `maxResults: 51` is a **parse error at authoring time**, not
262+
a silently-adjusted request.
263+
264+
```bash
265+
curl 'http://localhost:3000/api/v1/forms/contact-us/lookup/owner?q=ada'
266+
```
267+
268+
```json
269+
{ "data": [{ "id": "usr_1", "name": "Ada Lovelace" }],
270+
"total": 1, "truncated": false, "displayFields": ["name"] }
271+
```
272+
273+
Errors:
274+
275+
| Status | Code | When |
276+
|---|---|---|
277+
| `400 INVALID_REQUEST` | missing / blank slug or field |
278+
| `403 LOOKUP_NOT_PUBLIC` | the field has no `publicPicker` block — the deliberate loud default (#3022); also any server-managed anchor (`owner_id`, `organization_id`, …), which never gets a picker even if one is declared |
279+
| `404 FORM_NOT_FOUND` | slug not registered on any `sharing.allowAnonymous: true` view |
280+
| `500 LOOKUP_TARGET_MISSING` | the referenced object could not be resolved — declare `publicPicker.object` |
281+
235282
### Auth model
236283

237-
- Neither route calls `enforceAuth`, so they work under the always-on anonymous-deny default (there is no `requireAuth` knob to configure since v17).
238-
- The execution context handed to ObjectQL is `{ publicFormGrant: { object }, permissions: ['guest_portal'], anonymous: true }` with no `userId`. The Security plugin honors `publicFormGrant` first — a create + read-back grant scoped to exactly the declared object — so authorization holds even without a `guest_portal` profile. `permissions: ['guest_portal']` is retained for back-compat.
284+
- None of the three routes calls `enforceAuth`, so they work under the always-on anonymous-deny default (there is no `requireAuth` knob to configure since v17).
285+
- The execution context the **submit** route hands to ObjectQL is `{ publicFormGrant: { object }, permissions: ['guest_portal'], anonymous: true }` with no `userId`. The Security plugin honors `publicFormGrant` first — a create + read-back grant scoped to exactly the declared object — so authorization holds even without a `guest_portal` profile. `permissions: ['guest_portal']` is retained for back-compat. The **lookup** route's search context is `{ permissions: ['guest_portal'], anonymous: true }` — no `publicFormGrant` (it reads the picker's target object, not the form's), which is why its result set is bounded by the picker declaration instead.
239286
- No CSRF or auth header is needed; embed the form on any domain.
240287

241288
## 5. Embedding from a front-end

docs/audits/2026-06-viewschema-property-liveness.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Variant routing (`tabbed/wizard/split/drawer/modal`) exists in `plugin-form/Obje
1515
- **List common**: `name/label/type`, `columns` (legacy + rich), `filter`, `sort`, `searchableFields`, `filterableFields`, `userFilters{element,fields,tabs,showAllRecords}`, `selection`, `pagination`, `grouping`, `rowColor`, `rowHeight`, `hiddenFields/fieldOrder`, `rowActions`, `conditionalFormatting`, `inlineEdit`, `exportOptions`, `virtualScroll`, `tabs{icon,visible,pinned,filter}`, `emptyState`, `aria`, `appearance.allowedVisualizations`, `resizable/striped/bordered`, `showRecordCount`, `allowPrinting`. (evidence: `ObjectGrid.tsx`/`ListView.tsx`/`UserFilters.tsx`/`TabBar.tsx`)
1616
- **List variants** (all field-by-field wired, with deprecated flat aliases as fallback): **kanban** (`KanbanImpl.tsx:682`), **calendar** (`calendar-view-renderer.tsx:147`), **gantt** (`ObjectGantt.tsx:236`), **gallery** (`ObjectGallery.tsx:209`), **timeline** (`ObjectTimeline.tsx:179`).
1717
- **Form (where reachable)**: `sections`, FormField {`field,type,label,placeholder,required,readonly,hidden,options,reference,widget,colSpan,helpText,language,dependsOn,visibleOn,keyField,immutable`}, FormSection {`collapsible,collapsed,columns,visibleOn`}, `subforms` (master-detail, fully wired: `deriveMasterDetail.ts:338`, `LineItemsPanel.tsx:45`).
18+
- **Added post-audit (#7467, 2026-08-11)**: FormField `publicPicker{displayFields,maxResults,filter,object}` — live, consumer is `framework/packages/rest/src/rest-server.ts` `GET /forms/:slug/lookup/:field` (projection, hard cap 50, pre-filter, object override). The key had been enforced there but declared in no schema (the mirror of this audit's usual finding: enforced, never declarable), so no spec-valid form could reach it; declared per maintainer ruling on #7467. The route's fifth read, `publicPicker.sort`, stays deliberately undeclared (outside the ruling's enumeration; follow-up filed from #7467).
1819

1920
## DEAD — no consumer anywhere
2021
`userActions.buttons`, `addRecord.mode`, `addRecord.formView`, `sharing.lockedBy`, list-level `responsive`, list-level `performance`, FormView `submitBehavior{thankYou,redirect,continue,nextRecord}`, FormView `defaultSort`, FormView `sharing` (renderer side), `ViewData` providers `api` & `schema` in the **list** path, `tab.order` (not used for sorting).

docs/audits/2026-07-unknown-key-strictness-ledger.counts.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ regenerate.
2121
| Measure | Value |
2222
|---|---|
2323
| Triaged directories | 5 |
24-
| Object sites in them | 437 |
24+
| Object sites in them | 438 |
2525
| Still-open (strip) sites | 181 |
2626
| Files carrying at least one | 27 |
2727

@@ -44,12 +44,12 @@ The `strict` column is the one the campaign schedules against; it counts both th
4444

4545
| Dir | Sites | strict | passthrough | catchall | strip |
4646
|---|---|---|---|---|---|
47-
| `ui/` | 160 | 118 | 5 | 0 | 37 |
47+
| `ui/` | 161 | 119 | 5 | 0 | 37 |
4848
| `data/` | 165 | 56 | 1 | 0 | 108 |
4949
| `automation/` | 65 | 42 | 0 | 0 | 23 |
5050
| `security/` | 20 | 7 | 0 | 0 | 13 |
5151
| `studio/` | 27 | 27 | 0 | 0 | 0 |
52-
| **total** | **437** | **250** | **6** | **0** | **181** |
52+
| **total** | **438** | **251** | **6** | **0** | **181** |
5353

5454
## File-level triage — site counts
5555

@@ -75,9 +75,9 @@ classify and is not listed (it becomes reportable the day it grows its first sit
7575
| `responsive.zod.ts` | 4 |
7676
| `sharing.zod.ts` | 1 |
7777
| `theme.zod.ts` | 6 |
78-
| `view.zod.ts` | 53 |
78+
| `view.zod.ts` | 54 |
7979
| `widget.zod.ts` | 1 |
80-
| **total** | **160** |
80+
| **total** | **161** |
8181

8282
### `data/` — sites
8383

@@ -157,16 +157,16 @@ over it is here.
157157

158158
### `ui/` — open
159159

160-
**37 strip of 160**, in 5 file(s).
160+
**37 strip of 161**, in 5 file(s).
161161

162162
| File | Strip | Sites |
163163
|---|---|---|
164164
| `action-params.zod.ts` | 1 | 1 |
165165
| `app.zod.ts` | 1 | 18 |
166166
| `component.zod.ts` | 31 | 31 |
167-
| `view.zod.ts` | 3 | 53 |
167+
| `view.zod.ts` | 3 | 54 |
168168
| `widget.zod.ts` | 1 | 1 |
169-
| **total** | **37** | **160** |
169+
| **total** | **37** | **161** |
170170

171171
| Bucket | Sites |
172172
|---|---|

0 commit comments

Comments
 (0)