Releases: objectstack-ai/objectstack
Release list
@objectstack/hono@16.0.0
Patch Changes
- Updated dependencies [b39c65d]
- Updated dependencies [22013aa]
- Updated dependencies [fdc244e]
- Updated dependencies [bfa3c3f]
- Updated dependencies [2ea08ee]
- Updated dependencies [d1d1c40]
- Updated dependencies [ee0a499]
- Updated dependencies [62a2117]
- Updated dependencies [83e8f7d]
- Updated dependencies [efbcfe1]
- Updated dependencies [2049b6a]
- Updated dependencies [6c270a6]
- Updated dependencies [92f5f19]
- Updated dependencies [a2d6555]
- Updated dependencies [3a6310c]
- Updated dependencies [32899e6]
- Updated dependencies [515f11a]
- Updated dependencies [4174a07]
- Updated dependencies [ce468c8]
- @objectstack/runtime@16.0.0
- @objectstack/plugin-hono-server@16.0.0
- @objectstack/types@16.0.0
@objectstack/formula@16.0.0
Minor Changes
-
6b51346: feat(formula):
dateField == today()now matches — AST temporal-comparison rewrite (#3183)Behavior change (the fix): a
Field.datecompared with==/!=against a
temporal function now matches on the calendar day. Previously it silently
returned the wrong answer —record.due_date == today()was alwaysfalse
(and!= today()alwaystrue) even for a same-day record, because a
Field.datereads back as aYYYY-MM-DDstring (ADR-0053 Phase 1) and
cel-js's equality (overloads.jsisEqual) treats a string and a timestamp as
unequal without consulting any overload.celEngine.evaluatenow rewrites the parsed AST: for each==/!=whose one
operand istoday()/daysFromNow()/daysAgo()/now(), the field operand
is wrapped indate(...)(the stdlib coercion), then the expression is
serialized and evaluated. Sorecord.due_date == today()runs as
date(record.due_date) == today().- Per-occurrence, not per-field:
record.d == "2026-06-20" || record.d == today()
keeps the string-literal comparison intact while fixing the temporal one. - Type-blind-safe:
date()degrades gracefully — an already-Date
(Field.datetime) operand passes through; a non-date string or null →
Invalid Date→ the comparison staysfalse, exactly as before. No
field-type information is needed, and no currently-correct result is worsened. - Cheap: the rewrite only reserializes when such a comparison is present
(a plain-includesgate skips the rest), and is memoized per source string.
Applies to every interpreter site — read-time
Field.formula, default values,
validation rules, hook conditions, and flow conditions — since all route through
celEngine.evaluate. RLS/sharing conditions are unaffected: they compile via
cel-to-filter, which already rejects function calls as a loud authoring error.Supersedes the #3192 advisory lint. That build-time warning
(checkTemporalDateEquality) flaggeddateField == today()as a silent-miss;
with the runtime fixed it would be a false alarm, so it (and the
temporalEqualityFieldshelper it used) is removed. Authors can now write the
naturalrecord.due_date == today()directly; thedate(...)/
daysBetween(...) == 0/ range idioms all keep working. - Per-occurrence, not per-field:
-
80273c8: feat(formula): warn when a
datefield is compared to a temporal function with==/!=(#3183)A
Field.datedeserializes as aYYYY-MM-DDstring (ADR-0053 Phase 1), and
cel-js's equality hard-codesstring == <timestamp>tofalse— it returns
falsefor a string left operand without ever consulting a registered overload,
and refuses cross-type object equality (@marcbachmann/cel-jsoverloads.js
isEqual). So the most natural "is it due today" predicate —record.due_date == today() // silently false, even when due_date IS today record.due_date != today() // silently true for a same-day record— compiles clean, throws nothing, and silently never matches. Same silent-miss
family as #1928; timezone-independent (fails identically at UTC) and
cross-cutting (formulas, validation, RLS, flow/action/sharing/hook predicates).cel-js gives no operator-layer hook to fix the comparison, so this adds a
build-time advisory warning (the established ADR-0032 guardrail strategy)
rather than a runtime behavior change.validateExpressionreuses the shared
ExprSchemaHint.fieldTypes(the same per-field type map the #1928 tier-4
soundness check already threads through@objectstack/lint) to flag a==/!=
between adatefield (record./previous./bare) and
today()/daysFromNow()/daysAgo()/now(), with a self-correcting message
pointing at the working idioms:date(record.d) == today(), a range
(>= … && <= …), ordaysBetween(today(), record.d) == 0.Warning severity — never fails the build (the write/validation path may carry a
realDate). Restricted totype: 'date'(unambiguously a string);datetime
is excluded to avoid false positives. Ordering operators (>=/<=/</>)
already work — cel-js throws for them, tripping the engine's existing
string-hydration retry — so they are not flagged.A runtime fix (normalizing the peer of a temporal operand in the data layer)
remains tracked in #3183; a naive "hydrate date fields toDate" version would
trade this silent-miss for another (breakingdateField == "2026-06-20"), so it
needs its own design. -
7125007: Stored
Field.formulafields that compute dates/durations no longer silently evaluate tonull(#3306). Three independent CEL gaps made shipped template formulas (e.g.hr_employee.tenure_years,hr_time_off_request.days) returnnullwith no parse/build/runtime error:-
The null-guard idiom
cond ? <value> : nullnow compiles and evaluates. cel-js's ternary type-unifier rejects a concreteint/double/stringbranch againstnull— so eventrue ? 5 : nullfaulted "Ternary branches must have the same type" and the whole formula nulled. AField.formulais inherently nullable and the catalog blesses both ternary and== null, so this is the canonical "compute value, else blank" shape. An AST pre-pass (mirroring the #3183 temporal-equality rewrite) wraps the non-null branch indyn(...)— value-preserving, null-branch-only, idempotent — so it type-checks and runs. Applied incompile(),evaluate(), and the build soundness check alike. -
floor(x)/ceil(x)are now registered (parallel toround/abs) and advertised in the catalog. They round toward −∞ / +∞, sofloor(-1.2) == -2— NOT interchangeable with integer division's round-toward-zero. Previouslyfloor(...)faultedfound no matching overloadand the formula nulled. -
Date arithmetic is now a build-time ERROR instead of a silent runtime
null.record.end_date - record.start_date + 1,today() + 30,record.date + ntype-check clean (operands aredyn) but always fault at runtime and never recover (a date string is not numeric, so hydration can't rescue it). The build soundness check now typesdate/datetimefields asgoogle.protobuf.Timestampand flags date/duration arithmetic against a number with a corrective message pointing atdaysBetween(a, b)/daysFromNow(n)/addDays(d, n)/addMonths(d, n). Sound by construction — ordering (date < today(),date < "2026-01-01"string-lex), equality (#3183), and string concatenation ("Due: " + date) are all runtime-tolerated and never flagged; only arithmetic against a number is. A!= nullguard on a date field no longer masks the inner fault (== nullno-op overloads registered in the check-only env).
Heads-up for downstream: (3) adds a NEW build-time error. A stored formula or predicate doing arithmetic on a
date/datetimefield (end - start + 1,today() + 30) that previously built (and nulled at runtime) will now failobjectstack build/validateStackExpressionswith a message telling you to usedaysBetween/daysFromNow/addDays. This only fires for genuinely-broken expressions that already returnednull.Fixes #3306.
-
-
ea32ec7: feat(formula,lint): advisory type-soundness warnings for formula/predicate expressions (#1928 tier 4)
Closes the last open guardrail from #1928. A
Field.formulaor record-scoped
predicate that uses a text or boolean field with an arithmetic (+ - * / %)
or ordering (< > <= >=) operator against a number faults the runtime
overload and silently evaluates tonull(e.g.record.title * 2,
record.is_active + 1). The build now surfaces this as a non-blocking
warning with the offending field and a corrective message.Honours the ADR-0032 design law — the checker only flags what the runtime
would also fail:- Number / currency / percent / date / datetime fields are declared
dyn, so
the cases the runtime rescues never warn —record.amount / 100(the #1930
registerOperatorfix),record.due == today()and numeric-string / ISO-date
values (the string-hydration retry), and numeric-codedselectoption values. - Equality (
==/!=) is excluded: a heterogeneous equality is runtime-safe
(evaluates tofalse), never a fault.
New
firstTypeMismatch(source, fieldCelTypes, scope)export in
@objectstack/formula(and an optionalfieldTypeshint on
validateExpression);@objectstack/lint'svalidateStackExpressionsthreads
each object's field types into every checked site:- record-scoped sites (
record.<field>) — formula fields, validation rules,
action / hook / sharing predicates; - flattened flow / automation conditions (bare
field) — where flow
variables staydynand are never flagged, and equality stays runtime-safe.
Warnings are advisory in
objectstack build/validate(fatal only under
--strict), matching the tier-3 channel. - Number / currency / percent / date / datetime fields are declared
Patch Changes
-
e0859b1: fix(formula): retire the
jsexpression dialect and fix thehasDialectfalse-positive (#3278)The
jsexpression dialect was declared inExpressionDialectbut never
shipped — it existed only as a registry stub with no engine and no author helper
(cel/F/P→ CEL,tmpl→ template,cron→ cron; nothing ever emitted
js). Per ADR-0049 (enforce-or-remove) it is removed from the enum; the set is
now{cel, cron, template}.Procedural JavaScript is unaffected: it remains the L2 authoring surface —
the sandboxed, capability-gatedScriptBody { language: 'js' }in hook/action
bodies — which is a separate enum (hook-body.zod.ts), not an expression
dialect.Also fixes a latent bug in
hasDialect: it detected stubs via
dialect.startsWith('stub:'), but stubs were registered under their real name,
so the check was dead code andhasDialect('js')returned a f...
@objectstack/embedder-openai@16.0.0
Patch Changes
- Updated dependencies [f972574]
- Updated dependencies [6289ec3]
- Updated dependencies [22013aa]
- Updated dependencies [3ad3dd5]
- Updated dependencies [8efa395]
- Updated dependencies [3a18b60]
- Updated dependencies [a8aa34c]
- Updated dependencies [a3823b2]
- Updated dependencies [43a3efb]
- Updated dependencies [524696a]
- Updated dependencies [bfa3c3f]
- Updated dependencies [5e3301d]
- Updated dependencies [46e876c]
- Updated dependencies [158aa14]
- Updated dependencies [62a2117]
- Updated dependencies [d2723e2]
- Updated dependencies [fefcd54]
- Updated dependencies [beaf2de]
- Updated dependencies [369eb6e]
- Updated dependencies [06ff734]
- Updated dependencies [b659111]
- Updated dependencies [5754a23]
- Updated dependencies [6c270a6]
- Updated dependencies [668dd17]
- Updated dependencies [8abf133]
- Updated dependencies [e0859b1]
- Updated dependencies [04ecd4e]
- Updated dependencies [4d5a892]
- Updated dependencies [16cebeb]
- Updated dependencies [86d30af]
- Updated dependencies [8923843]
- Updated dependencies [a2795f6]
- Updated dependencies [f16b492]
- Updated dependencies [4b6fde8]
- Updated dependencies [2018df9]
- Updated dependencies [fc5a3a2]
- Updated dependencies [8ff9210]
- @objectstack/spec@16.0.0
@objectstack/driver-sqlite-wasm@16.0.0
Patch Changes
- Updated dependencies [f972574]
- Updated dependencies [6289ec3]
- Updated dependencies [22013aa]
- Updated dependencies [3ad3dd5]
- Updated dependencies [8efa395]
- Updated dependencies [3a18b60]
- Updated dependencies [a8aa34c]
- Updated dependencies [e057f42]
- Updated dependencies [a3823b2]
- Updated dependencies [43a3efb]
- Updated dependencies [524696a]
- Updated dependencies [bfa3c3f]
- Updated dependencies [5e3301d]
- Updated dependencies [dd9f223]
- Updated dependencies [47d923c]
- Updated dependencies [46e876c]
- Updated dependencies [5f05de2]
- Updated dependencies [021ba4c]
- Updated dependencies [158aa14]
- Updated dependencies [62a2117]
- Updated dependencies [d2723e2]
- Updated dependencies [fefcd54]
- Updated dependencies [efbcfe1]
- Updated dependencies [beaf2de]
- Updated dependencies [369eb6e]
- Updated dependencies [06ff734]
- Updated dependencies [b659111]
- Updated dependencies [5754a23]
- Updated dependencies [6c270a6]
- Updated dependencies [290e2f0]
- Updated dependencies [668dd17]
- Updated dependencies [8abf133]
- Updated dependencies [e0859b1]
- Updated dependencies [ce468c8]
- Updated dependencies [04ecd4e]
- Updated dependencies [4d5a892]
- Updated dependencies [16cebeb]
- Updated dependencies [86d30af]
- Updated dependencies [8923843]
- Updated dependencies [a2795f6]
- Updated dependencies [f16b492]
- Updated dependencies [4b6fde8]
- Updated dependencies [2018df9]
- Updated dependencies [fc5a3a2]
- Updated dependencies [8ff9210]
- @objectstack/spec@16.0.0
- @objectstack/core@16.0.0
- @objectstack/driver-sql@16.0.0
@objectstack/driver-sql@16.0.0
Minor Changes
-
efbcfe1: feat(observability): admin-only richer per-request timing detail via
X-OS-Debug-Timing: json(#2408)Completes the optional "richer JSON" diagnostic from #2408. In addition to the
basicServer-Timingheader, an admin/service caller can now request a
per-query breakdown — the slowest SQL statements and a query count — by sending
X-OS-Debug-Timing: json. The detail is returned in a separate
X-OS-Debug-Timing-Detailresponse header (compact JSON) and is admin-only,
even under global mode: an ordinary caller never sees SQL shapes.- observability:
PerfTiminggains opt-in per-event detail capture
(enableDetail/recordDetail/details) plus the ambient
recordServerTimingDetail. The disclosure gate gains aprivilegedlevel
(set byallowPerfDisclosure, read viaisPerfDisclosurePrivileged) so the
richer detail can be gated independently of the basic header. - driver-sql: when detail capture is on, the query listener additionally
records each query's parametrized statement (knex'sq.sql,?
placeholders) — never the bindings, so no literal row value ever enters the
collector. Zero overhead when detail is off. - plugin-hono-server:
X-OS-Debug-Timing: jsonenables detail capture; the
middleware emitsX-OS-Debug-Timing-Detail(slowest queries, capped and
sanitized to header-safe ASCII) only when the principal is a proven admin.
Basic and global behavior are unchanged;
jsonis purely additive. - observability:
Patch Changes
-
47d923c: fix(driver-sql): drop the vestigial
sqlite3peerDependency — the SQLite path usesbetter-sqlite3(#3277)package.jsonadvertisedpeerDependencies.sqlite3: "^5.0.0", but the driver never
loadssqlite3at runtime. Every first-party SQLite construction site builds a
client: 'better-sqlite3'Knex driver (resolveSqliteDriverin
@objectstack/service-datasource, the datasource driver factory, and the whole
driver test suite), and the README already tells consumers topnpm add better-sqlite3.
better-sqlite3is auto-provided as anoptionalDependency(with the native → wasm →
memory step-down of #2229 covering a failed native build), so the SQLite requirement is
already satisfied without the consumer installing anything.The stale
sqlite3peer only misled: a consumer resolving peer deps couldpnpm add sqlite3(never used) while believing they'd satisfied the SQLite requirement. Removing
it aligns the declared contract with the code and the docs. Thesqlite3string alias
still maps tobetter-sqlite3in the driver factory and dialect detection, so
driver: 'sqlite3'config keeps working — it just resolves tobetter-sqlite3like
everything else. -
ce468c8: feat(observability): decompose
Server-Timinginto auth / db / hooks / serialize spans (perf-tuning mode)The opt-in
Server-Timingheader now breaks a request's server time into the phases that actually explain it, so an operator can open DevTools → Network → Timing and see where the time went without standing up an external tracing backend:db— total SQL time with a query count. The SQL driver wires knex'squery/query-responseevents (keyed by__knexQueryUid) and folds each query into one aggregate member (db;dur=210;desc="6 queries") — the query count is the number most useful for spotting N sequential round-trips. Timing is attributed to the originating request viaAsyncLocalStorage, so it is correct under concurrency and never cross-attributes. SQL text is never emitted, only durations and a count.auth— identity / session resolution in the dispatcher, the prime suspect for unexplained data-API overhead.hooks— total business-hook execution time with a hook count, fed through the engine's existingHookMetricsRecorderseam (wired from the runtime, so@objectstack/objectql's leancoretier stays observability-free).serialize— response JSON encoding in the HTTP adapter.
Adds
countServerTiming(name, dur, unit)(andPerfTiming.count) to fold high-frequency phases into a single aggregate member instead of flooding the header. Every phase is a no-op when perf-tuning is off (serverTiming: true/OS_SERVER_TIMING=true), so there is zero measurable overhead on the normal path.Closes #2408.
-
86d30af: fix(tenancy): platform-global (
tenancy.enabled:false) objects are never driver-org-scoped (#3249)An org-context read of a platform-global object (e.g.
sys_license, ADR-0066)
could return 0 rows for an authenticated caller while an anonymous read saw the
data: the engine stampedexecCtx.tenantIdinto driver options unconditionally,
and the SQL driver's tenant-field cache could be re-corrupted to
organization_idby a partial re-registration (lifecycle archivesyncSchema,
schema-drift re-sync) whose schema omitted thetenancyblock.- New
isTenancyDisabled(schema)export from@objectstack/spec/data— the
single source of truth for the ADR-0066 platform-global posture, now shared by
the registry (tenant-column injection), the ObjectQL engine, and the SQL
driver. ObjectQL.buildDriverOptionsno longer stampstenantIdfor objects whose
registered schema declarestenancy.enabled: false(an explicitly-passed
optionstenantIdstill wins — deliberate caller intent).SqlDriver(andSqliteWasmDriver) now keep a sticky record of an explicit
tenancy.enabled:falsedeclaration: a later registration without atenancy
block preserves the opt-out instead of re-scoping via the implicit
organization_idheuristic; a registration that carries atenancy
declaration stays authoritative.
- New
-
Updated dependencies [f972574]
-
Updated dependencies [6289ec3]
-
Updated dependencies [22013aa]
-
Updated dependencies [3ad3dd5]
-
Updated dependencies [8efa395]
-
Updated dependencies [3a18b60]
-
Updated dependencies [a8aa34c]
-
Updated dependencies [e057f42]
-
Updated dependencies [a3823b2]
-
Updated dependencies [43a3efb]
-
Updated dependencies [524696a]
-
Updated dependencies [bfa3c3f]
-
Updated dependencies [5e3301d]
-
Updated dependencies [dd9f223]
-
Updated dependencies [46e876c]
-
Updated dependencies [5f05de2]
-
Updated dependencies [021ba4c]
-
Updated dependencies [158aa14]
-
Updated dependencies [62a2117]
-
Updated dependencies [83e8f7d]
-
Updated dependencies [d2723e2]
-
Updated dependencies [fefcd54]
-
Updated dependencies [efbcfe1]
-
Updated dependencies [2049b6a]
-
Updated dependencies [beaf2de]
-
Updated dependencies [369eb6e]
-
Updated dependencies [06ff734]
-
Updated dependencies [b659111]
-
Updated dependencies [5754a23]
-
Updated dependencies [6c270a6]
-
Updated dependencies [290e2f0]
-
Updated dependencies [668dd17]
-
Updated dependencies [8abf133]
-
Updated dependencies [e0859b1]
-
Updated dependencies [92f5f19]
-
Updated dependencies [32899e6]
-
Updated dependencies [ce468c8]
-
Updated dependencies [04ecd4e]
-
Updated dependencies [4d5a892]
-
Updated dependencies [16cebeb]
-
Updated dependencies [86d30af]
-
Updated dependencies [8923843]
-
Updated dependencies [a2795f6]
-
Updated dependencies [f16b492]
-
Updated dependencies [4b6fde8]
-
Updated dependencies [2018df9]
-
Updated dependencies [fc5a3a2]
-
Updated dependencies [8ff9210]
- @objectstack/spec@16.0.0
- @objectstack/core@16.0.0
- @objectstack/types@16.0.0
- @objectstack/observability@16.0.0
@objectstack/driver-mongodb@16.0.0
Patch Changes
- Updated dependencies [f972574]
- Updated dependencies [6289ec3]
- Updated dependencies [22013aa]
- Updated dependencies [3ad3dd5]
- Updated dependencies [8efa395]
- Updated dependencies [3a18b60]
- Updated dependencies [a8aa34c]
- Updated dependencies [e057f42]
- Updated dependencies [a3823b2]
- Updated dependencies [43a3efb]
- Updated dependencies [524696a]
- Updated dependencies [bfa3c3f]
- Updated dependencies [5e3301d]
- Updated dependencies [dd9f223]
- Updated dependencies [46e876c]
- Updated dependencies [5f05de2]
- Updated dependencies [021ba4c]
- Updated dependencies [158aa14]
- Updated dependencies [62a2117]
- Updated dependencies [d2723e2]
- Updated dependencies [fefcd54]
- Updated dependencies [beaf2de]
- Updated dependencies [369eb6e]
- Updated dependencies [06ff734]
- Updated dependencies [b659111]
- Updated dependencies [5754a23]
- Updated dependencies [6c270a6]
- Updated dependencies [290e2f0]
- Updated dependencies [668dd17]
- Updated dependencies [8abf133]
- Updated dependencies [e0859b1]
- Updated dependencies [04ecd4e]
- Updated dependencies [4d5a892]
- Updated dependencies [16cebeb]
- Updated dependencies [86d30af]
- Updated dependencies [8923843]
- Updated dependencies [a2795f6]
- Updated dependencies [f16b492]
- Updated dependencies [4b6fde8]
- Updated dependencies [2018df9]
- Updated dependencies [fc5a3a2]
- Updated dependencies [8ff9210]
- @objectstack/spec@16.0.0
- @objectstack/core@16.0.0
@objectstack/driver-memory@16.0.0
Patch Changes
- Updated dependencies [f972574]
- Updated dependencies [6289ec3]
- Updated dependencies [22013aa]
- Updated dependencies [3ad3dd5]
- Updated dependencies [8efa395]
- Updated dependencies [3a18b60]
- Updated dependencies [a8aa34c]
- Updated dependencies [e057f42]
- Updated dependencies [a3823b2]
- Updated dependencies [43a3efb]
- Updated dependencies [524696a]
- Updated dependencies [bfa3c3f]
- Updated dependencies [5e3301d]
- Updated dependencies [dd9f223]
- Updated dependencies [46e876c]
- Updated dependencies [5f05de2]
- Updated dependencies [021ba4c]
- Updated dependencies [158aa14]
- Updated dependencies [62a2117]
- Updated dependencies [d2723e2]
- Updated dependencies [fefcd54]
- Updated dependencies [beaf2de]
- Updated dependencies [369eb6e]
- Updated dependencies [06ff734]
- Updated dependencies [b659111]
- Updated dependencies [5754a23]
- Updated dependencies [6c270a6]
- Updated dependencies [290e2f0]
- Updated dependencies [668dd17]
- Updated dependencies [8abf133]
- Updated dependencies [e0859b1]
- Updated dependencies [04ecd4e]
- Updated dependencies [4d5a892]
- Updated dependencies [16cebeb]
- Updated dependencies [86d30af]
- Updated dependencies [8923843]
- Updated dependencies [a2795f6]
- Updated dependencies [f16b492]
- Updated dependencies [4b6fde8]
- Updated dependencies [2018df9]
- Updated dependencies [fc5a3a2]
- Updated dependencies [8ff9210]
- @objectstack/spec@16.0.0
- @objectstack/core@16.0.0
@objectstack/core@16.0.0
Minor Changes
-
dd9f223: feat(analytics): scope a datetime date-bucket drill to the reference-tz midnight instants (#1752 follow-up)
Closes the one gap left by the initial #1752 change: a
datetimedate dimension
bucketed under a non-UTC reference timezone previously fell back to a superset
drill (its bucket boundary is that tz's midnight instant, whichYYYY-MM-DD
calendar bounds can't express).@objectstack/coreaddszonedDateStartToUtcMs(ymd, tz)— the UTC instant
at which a calendar day begins in a reference timezone (the inverse of
calendarPartsInTz). DST-safe: the offset is read from the platform tz
database viaIntl, with a two-pass resolution for the rare offset-boundary
case; an unset/'UTC'/invalid zone returns plain UTC midnight.@objectstack/service-analyticsnow emitsdrillRangesbounds per the
field's temporal type (ADR-0053): adatetimefield → ISO instant bounds
at the reference tz's midnight (works under any tz, incl. DST); adatefield
→YYYY-MM-DDcalendar bounds (tz-naive, exact under any tz). An unknown field
type is still emitted only under UTC and omitted (superset) under a non-UTC tz.
No objectui change is needed — the client already forwards whatever bound values
the server sends into the drill filter and thefilter[field][gte|lt]URL. -
290e2f0: feat(analytics): emit a half-open date-range drill scope for granularity-bucketed date dimensions (#1752)
A report/dashboard cell grouped by a
dateGranularitydate dimension ("2026-Q2")
covers a SPAN of records, so drilling it needs a range (>= start AND < nextStart),
which the equality drill contract (drillRawRows) can't express — date dims were
therefore excluded from drill metadata and a drill landed on an unscoped superset.@objectstack/coreaddsbucketKeyToCalendarRange(key, granularity), the
inverse ofbucketDateValue: it turns a canonical bucket key into its half-open
[start, end)calendar span (YYYY-MM-DD,endexclusive). Pure, timezone-naive
calendar arithmetic; returnsnullfor unbucketable / out-of-range keys so the
caller falls back to an unscoped (superset) drill rather than emit a wrong bound.@objectstack/service-analyticsemits adrillRangessidecar (aligned to
rowsby index — the range companion todrillRawRows) fordate+
dateGranularitydimensions, computed from the canonical bucket key in the
pre-label-resolution snapshot pass. Adatetimefield under a non-UTC reference
timezone is omitted (host drills a superset) until instant-boundary support
lands; a tz-naivedatefield is exact under any timezone (ADR-0053).
Consumed by objectui's report drill-through to scope the drilled record list to the
clicked time bucket.
Patch Changes
-
e057f42: fix: harden the bulk-write path — retries, idempotency, contracts, and summary visibility (#3147–#3152)
Six reliability fixes to the batched seed/import +
engine.insert(array)path
introduced by the #2678 bulk-write rework:- #3151
bulkWritevalidates thatwriteBatchreturns one record per input
row (a short/long/non-array return is degraded per-row, not backfilled as
phantom success);engine.insert(array)likewise rejects a short driver
bulkCreatereturn instead of padding afterInsert withundefined. - #3150 wraps the two remaining un-retried write points (seed
writeRecord/resolveDeferredUpdates, import's no-createManyData
fallback) inwithTransientRetry;defaultIsTransientErrorshort-circuits
definitive logical errors to non-transient. - #3148 import
resolveRefflushes pending creates on a same-object miss so
a later row can reference an earlier same-file CREATE, and no longer
negatively caches a miss. - #3149 threads an
attemptcounter throughbulkWrite; seed rechecks by
externalIdand import bymatchFieldsbefore re-writing, so a
commit-then-lost-response retry cannot duplicate a batch. - #3147
recomputeSummariesretries transient failures and, on exhaustion,
surfacesSummaryRecomputeError(ERR_SUMMARY_RECOMPUTE) instead of a
silent warn; seed/import recover it to a warning without re-writing. - #3152 autonumbers are assigned after validation, so a batch that dies in
validation consumes no sequence value (no number-range gaps).
- #3151
-
5f05de2:
createLogger({ file })now actually writes the file under ESM.openFileStreamloadedfswith a lazyrequire()to keep the browser-safe logger entry out of thefsbundle graph; esbuild rewrites that to its__requireshim in the ESM output, which throwsDynamic require of "fs" is not supported, and a barecatch {}swallowed it. Since the workspace istype: module, every Node ESM consumer —os serve,os dev— silently got no file logging at all, while the CJS build kept working. The builtin now loads viaprocess.getBuiltinModule(opaque to bundlers, works in both module systems, with arequirefallback for Node < 20.16), and afiledestination that cannot be opened reports itself on stderr instead of disappearing.Turning the destination back on also fixed three faults that were unreachable while it never opened:
child()opened a second stream per child and orphaned it, destroying a child logger closed the stream its parent and siblings were still writing to, and an async open failure (e.g. an unwritable path) hit an'error'event with no listener and took the process down. -
021ba4c: fix(core): ObjectLogger honors NO_COLOR and TTY detection before emitting ANSI colors
The kernel/plugin logger (
ctx.logger, wired byos serve/os dev) colorized its
pretty-format level tags unconditionally, soNO_COLOR=1runs and piped/CI output
still carried ANSI escapes (e.g.\x1b[31m…ERROR\x1b[0m), breaking plain-text log
scanners (see scripts/publish-smoke.sh, which had to strip ANSI before grepping).Per the no-color.org convention, color is now emitted only when the destination stream
(stdout, or stderr for error/fatal) is an interactive TTY andNO_COLORis unset or
empty — any non-emptyNO_COLORvalue disables color. Interactive terminals keep the
existing colorized output. The optional file destination now always receives plain text. -
Updated dependencies [f972574]
-
Updated dependencies [6289ec3]
-
Updated dependencies [22013aa]
-
Updated dependencies [3ad3dd5]
-
Updated dependencies [8efa395]
-
Updated dependencies [3a18b60]
-
Updated dependencies [a8aa34c]
-
Updated dependencies [a3823b2]
-
Updated dependencies [43a3efb]
-
Updated dependencies [524696a]
-
Updated dependencies [bfa3c3f]
-
Updated dependencies [5e3301d]
-
Updated dependencies [46e876c]
-
Updated dependencies [158aa14]
-
Updated dependencies [62a2117]
-
Updated dependencies [d2723e2]
-
Updated dependencies [fefcd54]
-
Updated dependencies [beaf2de]
-
Updated dependencies [369eb6e]
-
Updated dependencies [06ff734]
-
Updated dependencies [b659111]
-
Updated dependencies [5754a23]
-
Updated dependencies [6c270a6]
-
Updated dependencies [668dd17]
-
Updated dependencies [8abf133]
-
Updated dependencies [e0859b1]
-
Updated dependencies [04ecd4e]
-
Updated dependencies [4d5a892]
-
Updated dependencies [16cebeb]
-
Updated dependencies [86d30af]
-
Updated dependencies [8923843]
-
Updated dependencies [a2795f6]
-
Updated dependencies [f16b492]
-
Updated dependencies [4b6fde8]
-
Updated dependencies [2018df9]
-
Updated dependencies [fc5a3a2]
-
Updated dependencies [8ff9210]
- @objectstack/spec@16.0.0
@objectstack/console@16.0.0
Minor Changes
-
bfa3c3f: Console (objectui) refreshed to
3b2e4d98d904. Frontend changes in this range:- fix(list): route remaining system-field groupings through shared classifier (#2706)
- feat(console): user-import wizard defaults to the
autopassword policy (tracks framework#3236) (#2701) - feat(flow-designer): schema-driven keyValue + numberList mapping (#3304) (#2708)
objectui range:
0318118e02fd...3b2e4d98d904 -
39b56d0: Console (objectui) refreshed to
94d4876df090. Frontend changes in this range:- feat(dashboard): Studio authors the ADR-0021 dataset shape only (framework#3251) (#2703)
- feat(app-shell): render ActionParamDialog params through the shared form field-widget renderer (#2700, ADR-0059) (#2704)
- feat(app-shell): distinguish writable system objects from engine-owned in badge + empty-state (ADR-0103 / #3220) (#2705)
- fix(list): keep injected owner_id out of leading auto-derived list columns (#2702)
- feat(flow-designer): #2670 Phase 3 — nested container node selection + schema-driven editing (#2699)
- feat(approvals-inbox): retire hardcoded secondary buttons for server-declared actions (#2697)
objectui range:
fd45313b4d00...94d4876df090 -
447465a: Console (objectui) refreshed to
e164196801bd. Frontend changes in this range:- fix(app-shell,plugin-detail): record History tab renders display values, not raw audit payloads (#2691)
- fix(plugin-gantt): mirror the row 「→」 slot in the task-list header (#2690)
- fix(plugin-detail): #2688 header Record-#id floor + raw audit user id in meta footer (#2689)
- feat(plugin-gantt)!: remove the mobile QR share (移动端二维码) context-menu feature (#2687)
- feat(plugin-gantt): dependencyTypes switch — hide the type switcher for id-only dependency stores (#2686)
- feat(approvals): decision attachments + progress display + deep link + designer sync (#2681)
- feat(studio): inline push-down expansion of loop/parallel/try_catch regions on the flow canvas (#2680)
- feat(plugin-gantt): ownership-aware reschedule + confirm-first auto-schedule, export fixes, business time zone (#2683)
- fix(app-shell): skip resultDialog fields whose path does not resolve (#2674)
- feat(studio): visualize loop/parallel/try_catch nested regions on the flow canvas (#2670) (#2675)
- feat(plugin-gantt): manual-scheduling summary bars, interaction switches, beforeTaskUpdate veto + tooltip/scrollbar/cursor fixes (#2677)
- fix(flow-designer): author the canonical config.schedule the runtime reads (#2671)
- feat(report): drill a date-bucket cell into its time range, not a superset (#1752) (#2672)
- feat(studio): filter editor for roll-up summary fields (framework#1868) (#2669)
- feat(flow-designer): first-class panel for the time-relative trigger (#1874) (#2668)
- feat(studio): nest per-iteration / per-region step logs in the flow Runs panel (#2667)
- fix(metadata-admin): dashboard label fallback + skill activation editors (#1878) (#2666)
objectui range:
2e7d7f0f7ee7...e164196801bd -
a140ff0: Console (objectui) refreshed to
fd45313b4d00. Frontend changes in this range:- feat(app-shell): DeclaredActionsBar — render server-declared object actions on bespoke pages (#2678 P2-4) (#2692)
- feat(data): unify master-detail saves behind DataSource.batchTransaction; isolate non-atomic fallback in the adapter (#2679) (#2684)
objectui range:
e164196801bd...fd45313b4d00
Patch Changes
-
a276969: Console (objectui) refreshed to
0318118e02fd. Frontend changes in this range:- fix(app-shell): guard ActionParamDialog submit during file upload + map spec
autonumber(ADR-0059 follow-ups) (#2707)
objectui range:
94d4876df090...0318118e02fd - fix(app-shell): guard ActionParamDialog submit during file upload + map spec
-
47d923c: Console (objectui) refreshed to
2e7d7f0f7ee7. Frontend changes in this range:- feat(evaluator): route CEL-dialect component/action predicates to the canonical engine (#2664)
- fix(grid): explain the import wizard's disabled Next and silent downgrade (#2640, #2639) (#2646)
- fix(form+detail): single-file children stay inline grids; drop non-spec
attachment(#2654, #2655) (#2656) - feat(access): localize curated capability labels client-side (#2600 B5 follow-up) (#2657)
- feat(access): localize capability picker group headers (#2600 B5, objectui side) (#2653)
- fix(access): Studio permission matrix — stop clipping the Bulk column at narrow widths (#2600 B3) (#2652)
- feat(access): Studio permission matrix — field-level bulk + filter for wide objects (#2600 B4) (#2651)
- feat(access): Studio Explain panel — package-scoped object dropdown instead of free-text api-name (#2600 B2) (#2650)
- feat(access): Studio permission matrix — collapse identity + zero-grant capabilities so the matrix hits the first screen (#2600 B1) (#2649)
- feat(plugin-list): 列表工具栏增加手动刷新按钮 (#2634) (#2645)
- fix(studio): approver Type dropdown drops deprecated
role, membership-tier picker (#2643) - fix(components): route internal html-page links through the SPA navigation handler (#2642)
- feat(discovery): trust only handlerReady/available services (ADR-0076 D12) (#2637)
- feat(types)!: adopt @objectstack/spec 15.1.1; drop value-erased spec/ui
…Schemare-exports (#2589) - feat(console): dev-seeded admin credentials hint on the login page (#2635)
- fix(auth): 注册页去掉重复的「or」分隔线(与 #2629 登录页修复对齐) (#2633)
- feat(app-shell/react): adapt to framework 15.1 — atomic publish rendering + honest discovery (#2630)
- fix(chatbot): plan approval flips the card to a Building… badge immediately (#2632)
- fix(app-shell,components): welcome CTA deep-links into the environment create dialog (#2631)
- fix(auth): login-page config race + sign-in watchdog — never strand SSO-only users on a password wall (#2629)
- feat(types): derive ListViewSchema from @objectstack/spec/ui (#2231) (#2622)
objectui range:
077e45b4bc55...2e7d7f0f7ee7 -
a791200: Console (objectui) refreshed to
69fa5d163a97. Frontend changes in this range:- fix(app-shell): mark notifications read via the REST surface, not direct receipt writes (#2743)
objectui range:
af1b0db96e44...69fa5d163a97 -
db34d54: Console (objectui) refreshed to
9a5f016f7d5c. Frontend changes in this range:- feat(flow-designer): nested-array columns in the node property form (#2678 P2-5) (#2761)
- fix: redo record-list "Add View" flow — empty-name 405, invisible drafts, canonical naming (#2768)
- feat(SchemaForm): field-type-aware operators + values for view filter (#2766)
- fix(plugin-charts): draw dashboard chart bars on first paint via isAnimationActive=false (#2756) (#2759)
- feat(data-objectstack): gate non-atomic batch fallback on discovery transactionalBatch capability (#2693) (#2755)
objectui range:
69fa5d163a97...9a5f016f7d5c -
1965549: Console (objectui) refreshed to
af1b0db96e44. Frontend changes in this range:- feat(i18n): localize action result dialogs via _actions..resultDialog (#2736)
- feat(data): thread the host's authenticated fetch into provider:'api' data sources (#2725) (#2732)
- feat(managedBy): add explicit
engine-ownedlifecycle bucket (tracks framework ADR-0103 addendum, #3343) (#2739) - feat(fields): CheckboxesField visibleWhen cascading + dependsOn gating (completes option-widget parity) (#2735)
- feat(fields): RadioField visibleWhen cascading + dependsOn gating; single-source the option resolver (#2728)
- fix(kanban,calendar): surface write failures instead of silently swallowing them (#2716)
- fix(plugin-charts): draw dashboard bars on first paint via one settle re-mount (#2727)
- feat(dashboard): retire pre-ADR-0021 inline-analytics renderer branches (framework#3320) (#2723)
- fix(data-objectstack): type the exportDownload test fetch mock so its type-check passes (#2726)
- feat(detail): related lists paginate by default with server-side $top/$skip windows (#2711) (#2722)
- fix(approvals-inbox): align participant gating with the server-computed viewer block (#2719)
- fix(plugin-view): coerce i18n tab-label helpers to string (TS2322) (#2721)
- feat(fields): MultiSelectField per-option visibleWhen cascading + dependsOn gating (#2715) (#2717)
- fix(site): make docs build resilient to remote badge fetch failures (#2695) (#2718)
- feat(approvals-inbox): retire the approve/reject composer for declared actions with file attachments (#2698) (#2710)
- feat(fields): select+multiple → multi-value chip picker; restore fields/core lint gates (#2709)
objectui range:
3b2e4d98d904...af1b0db96e44
@objectstack/connector-slack@16.0.0
Patch Changes
- Updated dependencies [f972574]
- Updated dependencies [6289ec3]
- Updated dependencies [22013aa]
- Updated dependencies [3ad3dd5]
- Updated dependencies [8efa395]
- Updated dependencies [3a18b60]
- Updated dependencies [a8aa34c]
- Updated dependencies [e057f42]
- Updated dependencies [a3823b2]
- Updated dependencies [43a3efb]
- Updated dependencies [524696a]
- Updated dependencies [bfa3c3f]
- Updated dependencies [5e3301d]
- Updated dependencies [dd9f223]
- Updated dependencies [46e876c]
- Updated dependencies [5f05de2]
- Updated dependencies [021ba4c]
- Updated dependencies [158aa14]
- Updated dependencies [62a2117]
- Updated dependencies [d2723e2]
- Updated dependencies [fefcd54]
- Updated dependencies [beaf2de]
- Updated dependencies [369eb6e]
- Updated dependencies [06ff734]
- Updated dependencies [b659111]
- Updated dependencies [5754a23]
- Updated dependencies [6c270a6]
- Updated dependencies [290e2f0]
- Updated dependencies [668dd17]
- Updated dependencies [8abf133]
- Updated dependencies [e0859b1]
- Updated dependencies [04ecd4e]
- Updated dependencies [4d5a892]
- Updated dependencies [16cebeb]
- Updated dependencies [86d30af]
- Updated dependencies [8923843]
- Updated dependencies [a2795f6]
- Updated dependencies [f16b492]
- Updated dependencies [4b6fde8]
- Updated dependencies [2018df9]
- Updated dependencies [fc5a3a2]
- Updated dependencies [8ff9210]
- @objectstack/spec@16.0.0
- @objectstack/core@16.0.0