Skip to content

Commit b8471d5

Browse files
os-zhuangclaude
andauthored
chore(showcase): seed Field Zoo with all field types + guard budget hook (#2005)
The Field Zoo declared all 48 field types but shipped NO seed data, so no field type was ever exercised with a real value — which is exactly why the array-serialization and Field.time bugs (fixed in #2004) went unnoticed. - Seed two field-zoo specimens ("Full" + "Minimal") covering every input-able field type with real values: arrays (multiselect/checkboxes/tags/repeater/ vector), objects (composite/address/location/json/record), time (14:30 / 09:05:30), relational (lookup→Account, master_detail→Project by externalId), and computed (formula=315, autonumber). `f_secret` is intentionally omitted — the seed path has no CryptoProvider so a secret value is refused fail-closed. This doubles as a runtime regression guard: a field type that can't persist now fails the boot seed instead of shipping silently. - Guard the showcase_warn_over_budget hook condition with has() — a partial rollup update (task_count recompute) fires afterUpdate with a record lacking spent/budget, and CEL throws "No such key" on a bare record.spent. has() is the missing-key-safe macro; the noisy warning is gone. Verified live: both specimens seed (37 populated f_* fields on Full); coverage test 20 green; warn_over_budget "No such key" warnings = 0 after a task update. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent bfa3102 commit b8471d5

3 files changed

Lines changed: 53 additions & 2 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
---
3+
4+
chore(showcase): seed the Field Zoo with two specimens exercising every input-able field type (arrays/objects/time/relational/computed all populated), and guard the `warn_over_budget` hook condition with `has()` so a partial rollup update no longer logs a "No such key" warning. The seed doubles as a runtime regression guard for the field-type persistence fixes — a field type that can't store now fails the boot seed instead of shipping silently. Example-app only; no package impact.

examples/app-showcase/src/data/index.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Task } from '../objects/task.object.js';
88
import { Category } from '../objects/category.object.js';
99
import { Team, ProjectMembership } from '../objects/team.object.js';
1010
import { Product } from '../objects/invoice.object.js';
11+
import { FieldZoo } from '../objects/field-zoo.object.js';
1112

1213
/**
1314
* Seed data sized to "feed every view": every Kanban column is populated,
@@ -100,4 +101,46 @@ const memberships = defineSeed(ProjectMembership, {
100101
],
101102
});
102103

103-
export const ShowcaseSeedData = [accounts, products, projects, tasks, categories, teams, memberships];
104+
// Field Zoo specimens — one record per "look" exercising EVERY input-able
105+
// field type with a real value. This is both a demo (the gallery finally
106+
// renders populated) and a regression guard: the seed inserts at boot, so a
107+
// field type that can't persist (e.g. the array-serialization / `time` bugs
108+
// found here) makes the app fail to start instead of silently shipping broken.
109+
// Relational/computed fields (lookup/master_detail/tree/record-map, formula/
110+
// summary/autonumber) resolve or generate at runtime. `f_master_detail` is the
111+
// owning Project; `f_lookup` the Account — referenced by their seed externalId.
112+
const fieldZoo = defineSeed(FieldZoo, {
113+
mode: 'upsert',
114+
externalId: 'name',
115+
records: [
116+
{
117+
name: 'Specimen — Full',
118+
f_textarea: 'Line one\nLine two', f_email: 'zoo@example.com', f_url: 'https://objectstack.ai',
119+
// NOTE: `f_secret` (encryption-at-rest) is intentionally omitted — the
120+
// seed path has no CryptoProvider, so a secret value is (correctly)
121+
// refused fail-closed. The field type is still covered by the schema.
122+
f_phone: '+1 555 010 2030', f_password: 'hunter2',
123+
f_markdown: '# Heading\n\n- a\n- b', f_html: '<b>bold</b> & <i>italic</i>', f_richtext: '<p>Rich <strong>text</strong></p>',
124+
f_number: 420, f_currency: 1299.95, f_percent: 75,
125+
f_date: '2026-06-17', f_datetime: '2026-06-17T14:30:00Z', f_time: '14:30',
126+
f_boolean: true, f_toggle: true,
127+
f_select: 'high', f_multiselect: ['red', 'green'], f_radio: 'yes', f_checkboxes: ['email', 'push'], f_tags: ['alpha', 'beta'],
128+
f_lookup: 'Northwind', f_master_detail: 'Website Relaunch',
129+
f_location: { lat: 47.6062, lng: -122.3321 }, f_address: { street: '1 Main St', city: 'Seattle', state: 'WA', postal_code: '98101', country: 'US' },
130+
f_code: '{\n "ok": true\n}', f_json: { nested: { k: 'v' }, list: [1, 2, 3] }, f_color: '#2563EB',
131+
f_rating: 4, f_slider: 60, f_progress: 80,
132+
f_composite: { width: 10, height: 20 }, f_repeater: [{ label: 'one', qty: 1 }, { label: 'two', qty: 2 }],
133+
f_record: { primary: { name: 'A', score: 9 }, backup: { name: 'B', score: 7 } },
134+
f_vector: [0.12, 0.34, 0.56, 0.78],
135+
},
136+
{
137+
name: 'Specimen — Minimal',
138+
f_number: 7, f_select: 'low', f_radio: 'no', f_time: '09:05:30',
139+
f_multiselect: ['blue'], f_checkboxes: ['sms'], f_tags: [],
140+
f_boolean: false, f_rating: 2, f_slider: 0, f_progress: 0,
141+
f_master_detail: 'Data Platform',
142+
},
143+
],
144+
});
145+
146+
export const ShowcaseSeedData = [accounts, products, projects, tasks, categories, teams, memberships, fieldZoo];

examples/app-showcase/src/hooks/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,11 @@ export const WarnOverBudgetHook = {
6363
label: 'Warn On Over-Budget Project',
6464
object: 'showcase_project',
6565
events: ['afterUpdate'] as LifecycleEvent[],
66-
condition: "record.spent > record.budget",
66+
// Guard with has(): an afterUpdate fired by a partial write (e.g. the
67+
// task-rollup that only touches task_count) carries a record WITHOUT
68+
// spent/budget, and CEL throws "No such key" on a bare `record.spent`.
69+
// has() is the missing-key-safe macro — the hook simply skips those.
70+
condition: "has(record.spent) && has(record.budget) && record.spent > record.budget",
6771
body: {
6872
language: 'js' as const,
6973
source: "var r = ctx.result || ctx.input || {}; if (typeof ctx.log === 'function') ctx.log('project over budget: ' + (r.name || r.id || 'unknown') + ' (' + r.spent + ' / ' + r.budget + ')');",

0 commit comments

Comments
 (0)