chore: version packages#2040
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@objectstack/formula@9.10.0
Minor Changes
addDays(date, n)andaddMonths(date, n)to the CEL standard library — shift an arbitrary date by a (possibly negative) number of days or months. UnlikedaysFromNow, these operate on a given date (the "next service date = last service + cycle" shape).addMonthsclamps to the target month's last day (addMonths(date('2026-01-31'), 1)→ Feb 28, never overflowing into March). Both coerce their inputs (Date | ISO string | epoch) and typenasdynso a record number field arriving as adoubledoesn't faultno such overload(Formula guardrail: cel-js arithmetic silently returns null (double × int + bare identifiers) #1928).Patch Changes
@objectstack/objectql@9.10.0
Minor Changes
1f88fd9: Add a transaction boundary to sandboxed hook/action bodies:
ctx.api.transaction(async () => { … }). Everyctx.apiread/write inside the callback runs in one driver transaction — committed when the callback returns, rolled back if it throws (or if the body leaves the transaction open at timeout). Guarded by the newapi.transactioncapability.api.transactioncapability token onHookBodyCapability.ScopedContextgains discretebeginTransaction()/commitTransaction(handle)/rollbackTransaction(handle)primitives. The handle is threaded explicitly through a child context (resolveTxhonors it ahead of the ambienttxStore), because the sandbox drives the body across many host event-loop turns where AsyncLocalStorage context does not survive. Degrades to non-transactional execution when the driver has no transaction support.ctx.api.transactionover three deferred-promise host leaves (begin/commit/rollback), routes in-transaction ops through the tx-scoped context, and rolls back a transaction the body left open before disposing the VM.e2b5324: feat(ownership): auto-provision a canonical
owner_idand hand seeded records to the first adminOwnership is now correct-by-default instead of opt-in — closing the gap where
seeded demo data ended up owned by nobody a human can log in as (so "My" views,
owner reports and owner notifications were empty out of the box) and where
author-written objects silently shipped with no working ownership at all.
applySystemFields(objectql) now auto-injects a canonical, reassignableowner_idlookup (→sys_user) on user-authored business objects, alongsidethe existing tenant/audit fields. Unlike the audit
*_bylookups it is NOTreadonly — ownership transfers. Withheld for
managedBy/sys_*tables andfor objects that opt out via
ownership: 'org' | 'none'(Dataverse-style). Thesafe default direction: forgetting the opt-out leaves a harmless spare column,
whereas the old opt-IN model let authors ship objects with broken ownership.
Once present, the existing machinery engages automatically (insert auto-stamp,
owner-scoped RLS, owner-keyed views/reports).
claimSeedOwnership(plugin-security), invoked frombootstrapPlatformAdminright after the first human is promoted to platform admin, transfers ownership
of seeded rows (
owner_idNULL orusr_system) to that admin. The ownershiptwin of org-scoping's
claimOrphanOrgRows. Idempotent; skipsmanagedBy/sys_*. Authors write plain seed records (noowner_id) and the platform —not the author — performs the handoff, so there is nothing to remember or
mistype.
usr_systemis never minted (runtime + objectql). The seed loader bindsos.userto a NULL identity, socelos.user.id``resolves to NULL at seedtime (the owning admin does not exist yet) and the row seeds NULL-owned — then
the handoff above fills it. The runtime's
ensureSeedIdentity(the only codethat inserted a`usr_system`row) is removed.`SystemUserId.SYSTEM`survives
only as a reserved id so legacy DBs' exclusion guards / ownership handoff still
recognize a pre-existing row.`os.org`is unaffected (derived from`organizationId`).
Also hardens
bootstrapPlatformAdminagainst a latent dts typecheck error(defensive read of the untyped
descriptionon seed permission sets).Patch Changes
fd07027: fix(analytics): make organization timezone actually drive date-dimension bucketing (ADR-0053 Phase 2, ADR-0053 Phase 2 · Slice 5: timezone-aware analytics date bucketing #1982)
Date-bucketed analytics silently ignored the reference timezone end-to-end. Three independent seams were broken:
NativeSQLStrategy(priority 10) won every cube/dataset query on a SQL driver, but it groups by the raw column (nodate_trunc) and ignorestimezone, so a date dimension never bucketed (one row per raw timestamp) and a non-UTC zone was dropped. It now declines queries that carry atimeDimensions[].granularity, handing them toObjectQLStrategy→engine.aggregate(native bucketing when UTC-safe, uniform in-memory bucketing when non-UTC).countaggregation treated the*count-all sentinel (the Cubecountmeasure / a fieldless datasetcount, both compiled tosql: '*') as a column name, counting non-null of a non-existent property →0for every bucket. The driver'sCOUNT(*)masked it; the in-memory path (non-UTC date buckets,driver-rest/driver-memory) returned zeros.*is now counted as all rows.resolveExecCtxnever resolved the localization timezone/locale, so/analytics/dataset/queryalways ran withtimezone: 'UTC'. It now resolves them through thesettingsservice (honouring the 4-tier cascade incl. theOS_LOCALIZATION_TIMEZONEenv override), mirroring the dispatcher path.Updated dependencies [db02bd5]
Updated dependencies [641675d]
Updated dependencies [1f88fd9]
Updated dependencies [94e9040]
Updated dependencies [1f88fd9]
Updated dependencies [1f88fd9]
@objectstack/plugin-org-scoping@9.10.0
Minor Changes
f169558: feat(org-scoping): hand a default org's seeded records to its admin (multi-tenant ownership handoff)
The multi-tenant companion to plugin-security's single-tenant
claimSeedOwnership.Seeded rows land
owner_idNULL (the author leaves it unset;celos.user.id``resolves to NULL at seed time). In multi-tenant modeclaimOrphanOrgRowsback-fills their`organization_id`, but `owner_id` stayed NULL — so "My" views,owner reports and owner notifications were empty for the org's members.
claimOrgSeedOwnership(ql, organizationId, ownerUserId)— assignsowner_id = ownerUserIdto an org's NULL-owned seed rows. Scoped to a singleorg (never touches another tenant), idempotent, skips
managedBy/sys_*,and requires both
owner_idandorganization_idcolumns.ensureDefaultOrganizationnow calls it after binding the platform admin asthe default org's owner, so the default org's demo data is owned by the admin
out of the box — symmetric with the single-tenant first-admin handoff.
Patch Changes
@objectstack/plugin-security@9.10.0
Minor Changes
1f88fd9: Converge the RLS contract with the reference compiler, and wire §7.3.1 dynamic membership.
rls.zod.tsto the four expression forms the compiler actually implements —field = current_user.<prop>,field = 'literal',field IN (current_user.<array>), and1 = 1. Removed the over-promised surface (subqueries,AND/OR/NOT,LIKE/ILIKE, regex,ANY/ALL,NOT IN,IS NULL,NOW()/CURRENT_DATE) from the operator list, context-variable list, and@examplepolicies, and documented the fail-closed behaviour explicitly.ExecutionContextgainsrlsMembership?: Record<string, string[]>— a bag of pre-resolved dynamic-membership id arrays (team members, territory accounts, shared records) that the runtime stages so RLS can scope viafield IN (current_user.<key>)without subquery support. Generalizes the previously hard-codedorg_user_ids.RLSCompiler.compileFiltermergesrlsMembershipkeys into the user context (arrays only, never clobbering the namedid/organization_id/roles/org_user_idsfields), so §7.3.1 hierarchy- and sharing-based policies compile.compileExpressionnow recognizes1 = 1as always-true (empty filter), makingRLS.allowAllPolicygrant access instead of silently failing closed. Missing/empty membership sets still fail closed.e2b5324: feat(ownership): auto-provision a canonical
owner_idand hand seeded records to the first adminOwnership is now correct-by-default instead of opt-in — closing the gap where
seeded demo data ended up owned by nobody a human can log in as (so "My" views,
owner reports and owner notifications were empty out of the box) and where
author-written objects silently shipped with no working ownership at all.
applySystemFields(objectql) now auto-injects a canonical, reassignableowner_idlookup (→sys_user) on user-authored business objects, alongsidethe existing tenant/audit fields. Unlike the audit
*_bylookups it is NOTreadonly — ownership transfers. Withheld for
managedBy/sys_*tables andfor objects that opt out via
ownership: 'org' | 'none'(Dataverse-style). Thesafe default direction: forgetting the opt-out leaves a harmless spare column,
whereas the old opt-IN model let authors ship objects with broken ownership.
Once present, the existing machinery engages automatically (insert auto-stamp,
owner-scoped RLS, owner-keyed views/reports).
claimSeedOwnership(plugin-security), invoked frombootstrapPlatformAdminright after the first human is promoted to platform admin, transfers ownership
of seeded rows (
owner_idNULL orusr_system) to that admin. The ownershiptwin of org-scoping's
claimOrphanOrgRows. Idempotent; skipsmanagedBy/sys_*. Authors write plain seed records (noowner_id) and the platform —not the author — performs the handoff, so there is nothing to remember or
mistype.
usr_systemis never minted (runtime + objectql). The seed loader bindsos.userto a NULL identity, socelos.user.id``resolves to NULL at seedtime (the owning admin does not exist yet) and the row seeds NULL-owned — then
the handoff above fills it. The runtime's
ensureSeedIdentity(the only codethat inserted a`usr_system`row) is removed.`SystemUserId.SYSTEM`survives
only as a reserved id so legacy DBs' exclusion guards / ownership handoff still
recognize a pre-existing row.`os.org`is unaffected (derived from`organizationId`).
Also hardens
bootstrapPlatformAdminagainst a latent dts typecheck error(defensive read of the untyped
descriptionon seed permission sets).Patch Changes
@objectstack/runtime@9.10.0
Minor Changes
1f88fd9: Add a transaction boundary to sandboxed hook/action bodies:
ctx.api.transaction(async () => { … }). Everyctx.apiread/write inside the callback runs in one driver transaction — committed when the callback returns, rolled back if it throws (or if the body leaves the transaction open at timeout). Guarded by the newapi.transactioncapability.api.transactioncapability token onHookBodyCapability.ScopedContextgains discretebeginTransaction()/commitTransaction(handle)/rollbackTransaction(handle)primitives. The handle is threaded explicitly through a child context (resolveTxhonors it ahead of the ambienttxStore), because the sandbox drives the body across many host event-loop turns where AsyncLocalStorage context does not survive. Degrades to non-transactional execution when the driver has no transaction support.ctx.api.transactionover three deferred-promise host leaves (begin/commit/rollback), routes in-transaction ops through the tx-scoped context, and rolls back a transaction the body left open before disposing the VM.e2b5324: feat(ownership): auto-provision a canonical
owner_idand hand seeded records to the first adminOwnership is now correct-by-default instead of opt-in — closing the gap where
seeded demo data ended up owned by nobody a human can log in as (so "My" views,
owner reports and owner notifications were empty out of the box) and where
author-written objects silently shipped with no working ownership at all.
applySystemFields(objectql) now auto-injects a canonical, reassignableowner_idlookup (→sys_user) on user-authored business objects, alongsidethe existing tenant/audit fields. Unlike the audit
*_bylookups it is NOTreadonly — ownership transfers. Withheld for
managedBy/sys_*tables andfor objects that opt out via
ownership: 'org' | 'none'(Dataverse-style). Thesafe default direction: forgetting the opt-out leaves a harmless spare column,
whereas the old opt-IN model let authors ship objects with broken ownership.
Once present, the existing machinery engages automatically (insert auto-stamp,
owner-scoped RLS, owner-keyed views/reports).
claimSeedOwnership(plugin-security), invoked frombootstrapPlatformAdminright after the first human is promoted to platform admin, transfers ownership
of seeded rows (
owner_idNULL orusr_system) to that admin. The ownershiptwin of org-scoping's
claimOrphanOrgRows. Idempotent; skipsmanagedBy/sys_*. Authors write plain seed records (noowner_id) and the platform —not the author — performs the handoff, so there is nothing to remember or
mistype.
usr_systemis never minted (runtime + objectql). The seed loader bindsos.userto a NULL identity, socelos.user.id``resolves to NULL at seedtime (the owning admin does not exist yet) and the row seeds NULL-owned — then
the handoff above fills it. The runtime's
ensureSeedIdentity(the only codethat inserted a`usr_system`row) is removed.`SystemUserId.SYSTEM`survives
only as a reserved id so legacy DBs' exclusion guards / ownership handoff still
recognize a pre-existing row.`os.org`is unaffected (derived from`organizationId`).
Also hardens
bootstrapPlatformAdminagainst a latent dts typecheck error(defensive read of the untyped
descriptionon seed permission sets).Patch Changes
@objectstack/spec@9.10.0
Minor Changes
1f88fd9: Converge the RLS contract with the reference compiler, and wire §7.3.1 dynamic membership.
rls.zod.tsto the four expression forms the compiler actually implements —field = current_user.<prop>,field = 'literal',field IN (current_user.<array>), and1 = 1. Removed the over-promised surface (subqueries,AND/OR/NOT,LIKE/ILIKE, regex,ANY/ALL,NOT IN,IS NULL,NOW()/CURRENT_DATE) from the operator list, context-variable list, and@examplepolicies, and documented the fail-closed behaviour explicitly.ExecutionContextgainsrlsMembership?: Record<string, string[]>— a bag of pre-resolved dynamic-membership id arrays (team members, territory accounts, shared records) that the runtime stages so RLS can scope viafield IN (current_user.<key>)without subquery support. Generalizes the previously hard-codedorg_user_ids.RLSCompiler.compileFiltermergesrlsMembershipkeys into the user context (arrays only, never clobbering the namedid/organization_id/roles/org_user_idsfields), so §7.3.1 hierarchy- and sharing-based policies compile.compileExpressionnow recognizes1 = 1as always-true (empty filter), makingRLS.allowAllPolicygrant access instead of silently failing closed. Missing/empty membership sets still fail closed.1f88fd9: Add a transaction boundary to sandboxed hook/action bodies:
ctx.api.transaction(async () => { … }). Everyctx.apiread/write inside the callback runs in one driver transaction — committed when the callback returns, rolled back if it throws (or if the body leaves the transaction open at timeout). Guarded by the newapi.transactioncapability.api.transactioncapability token onHookBodyCapability.ScopedContextgains discretebeginTransaction()/commitTransaction(handle)/rollbackTransaction(handle)primitives. The handle is threaded explicitly through a child context (resolveTxhonors it ahead of the ambienttxStore), because the sandbox drives the body across many host event-loop turns where AsyncLocalStorage context does not survive. Degrades to non-transactional execution when the driver has no transaction support.ctx.api.transactionover three deferred-promise host leaves (begin/commit/rollback), routes in-transaction ops through the tx-scoped context, and rolls back a transaction the body left open before disposing the VM.Patch Changes
db02bd5: Fix dashboard time-series charts / "last N months" KPIs that filter or group by a
Field.datetimecolumn silently returning "No rows".The analytics
NativeSQLStrategycompiles dashboard relative-date tokens ({12_months_ago},{today}, …) to ISO date strings and binds them directly into raw SQL, bypassing the driver's own filter coercion. Under better-sqlite3 aField.datetimecolumn is stored as an INTEGER epoch (ms), soassessed_at >= '2025-06-18'became a TEXT-vs-INTEGER affinity compare that is always false — an empty result even though the rows exist.Field.datecolumns store ISO TEXT and were unaffected.The strategy now coerces a temporal comparand to the column's on-disk storage form via a new optional
StrategyContext.coerceTemporalFilterValuehook, wired to the driver's publicSqlDriver.temporalFilterValue(the single source of truth for the storage convention). Coercion is dialect-correct: SQLiteField.datetime→ epoch ms;Field.datetext and native-timestamp dialects (Postgres/MySQL) are left unchanged, so Postgres is never handed an epoch integer. Applied togte/lte/gt/lt/equals,in/notIn, and thedateRange/timeDimensionBETWEENpath.641675d: Add
*Inputauthoring-type aliases (DatasourceInput,ConnectorInput,SharingRuleInput,JobInput,WebhookInput,EmailTemplateDefinitionInput,RoleInput,PermissionSetInput,ObjectExtensionInput) alongside the existingFieldInput/ActionInput/ReportInput/PortalInputconvention. These arez.input<typeof XSchema>aliases so authored literals keep.default()fields optional and accept CEL/Expression string shorthands — matching howdefineX()helpers already accept input. No runtime change.94e9040: fix(spec): declare the extended Gantt config fields the renderer actually reads
GanttConfigSchemaonly declared the 5 core timeline fields as a plainz.object(no passthrough), so every other field the Gantt renderer consumes —parentField/typeField(two-level summary→step hierarchy),colorField,groupByField,tooltipFields,baselineStartField/baselineEndField,resourceView/assigneeField/effortField/capacity,quickFilters,autoZoomToFilter— was silently stripped by.parse()on both the compile-timeprotocol check and the runtime
GET /api/v1/meta/view/:objectre-validation. Withthe keys gone before render, the Gantt degraded to a flat list (no parent/child
rows, no summary bars, no expand/collapse). These fields are now declared
explicitly (with descriptions), so the renderer contract round-trips through the
spec instead of requiring downstream patches.
@objectstack/express@9.10.0
Patch Changes
@objectstack/fastify@9.10.0
Patch Changes
@objectstack/hono@9.10.0
Patch Changes
@objectstack/nestjs@9.10.0
Patch Changes
@objectstack/nextjs@9.10.0
Patch Changes
@objectstack/nuxt@9.10.0
Patch Changes
@objectstack/sveltekit@9.10.0
Patch Changes
@objectstack/account@9.10.0
Patch Changes
@objectstack/setup@9.10.0
Patch Changes
@objectstack/studio@9.10.0
Patch Changes
@objectstack/cli@9.10.0
Patch Changes
@objectstack/client@9.10.0
Patch Changes
@objectstack/client-react@9.10.0
Patch Changes
@objectstack/cloud-connection@9.10.0
Patch Changes
@objectstack/connector-mcp@9.10.0
Patch Changes
@objectstack/connector-openapi@9.10.0
Patch Changes
@objectstack/connector-rest@9.10.0
Patch Changes
@objectstack/connector-slack@9.10.0
Patch Changes
@objectstack/core@9.10.0
Patch Changes
@objectstack/mcp@9.10.0
Patch Changes
@objectstack/metadata@9.10.0
Patch Changes
@objectstack/metadata-core@9.10.0
Patch Changes
@objectstack/metadata-fs@9.10.0
Patch Changes
@objectstack/observability@9.10.0
Patch Changes
@objectstack/platform-objects@9.10.0
Patch Changes
end_user_controlstranslations for en, es-ES, ja-JP and zh-CN metadata-forms bundles.@objectstack/driver-memory@9.10.0
Patch Changes
@objectstack/driver-mongodb@9.10.0
Patch Changes
@objectstack/driver-sql@9.10.0
Patch Changes
db02bd5: Fix dashboard time-series charts / "last N months" KPIs that filter or group by a
Field.datetimecolumn silently returning "No rows".The analytics
NativeSQLStrategycompiles dashboard relative-date tokens ({12_months_ago},{today}, …) to ISO date strings and binds them directly into raw SQL, bypassing the driver's own filter coercion. Under better-sqlite3 aField.datetimecolumn is stored as an INTEGER epoch (ms), soassessed_at >= '2025-06-18'became a TEXT-vs-INTEGER affinity compare that is always false — an empty result even though the rows exist.Field.datecolumns store ISO TEXT and were unaffected.The strategy now coerces a temporal comparand to the column's on-disk storage form via a new optional
StrategyContext.coerceTemporalFilterValuehook, wired to the driver's publicSqlDriver.temporalFilterValue(the single source of truth for the storage convention). Coercion is dialect-correct: SQLiteField.datetime→ epoch ms;Field.datetext and native-timestamp dialects (Postgres/MySQL) are left unchanged, so Postgres is never handed an epoch integer. Applied togte/lte/gt/lt/equals,in/notIn, and thedateRange/timeDimensionBETWEENpath.d9508d1: fix(driver-sql): make numeric-scalar type fidelity self-heal on legacy SQLite columns
The fix(driver-sql): round-trip rating/slider/toggle with type fidelity #2025 fix mapped
rating/slider/progressto numeric columns, but SQLite never alters a column's type in place and the schema reconciler only adds missing columns — so a column created before that fix keeps its TEXT affinity and would still read back'4'instead of4forever.A read-side numeric coercion (the new
numericFieldsregistry, single-sourced fromNUMERIC_SCALAR_TYPES) now coerces numeric-looking stored strings back to numbers on read, mirroring howdateFieldsalready repairs legacy timestamp-typedField.daterows. The fidelity no longer depends on column affinity alone;nulland genuinely non-numeric legacy values are left intact rather than turned into0/NaN.1d352d3: fix(driver-sql): round-trip rating/slider/toggle/progress with type fidelity
rating/slider/toggle/progresshad no case in the DDL column-type switch, so they fell todefault → table.string(TEXT affinity). SQLite then coerced the written value to a string —rating: 4read back'4',toggle: trueread back'1'— so the value persisted but the JS type leaked on read. On a low-code platform where field types are author-driven, a field that silently returns the wrong type is a runtime-fidelity trap the static gates and value-loss tests don't catch.rating/slider/progressnow map to a REAL (numeric) column.togglemaps to a boolean column and is registered in the boolean read-coercion path, so stored1/0come back as real JS booleans.record/video/audiotypes are folded into the sharedJSON_COLUMN_TYPESsource, and the DDLdefaultcase now derives JSON-vs-string from that set, so the column-type switch andisJsonField(the read-side deserializer) can no longer drift.Updated dependencies [db02bd5]
Updated dependencies [641675d]
Updated dependencies [94e9040]
Updated dependencies [1f88fd9]
Updated dependencies [1f88fd9]
@objectstack/driver-sqlite-wasm@9.10.0
Patch Changes
@objectstack/embedder-openai@9.10.0
Patch Changes
@objectstack/knowledge-memory@9.10.0
Patch Changes
@objectstack/knowledge-ragflow@9.10.0
Patch Changes
@objectstack/plugin-approvals@9.10.0
Patch Changes
@objectstack/plugin-audit@9.10.0
Patch Changes
@objectstack/plugin-auth@9.10.0
Patch Changes
@objectstack/plugin-dev@9.10.0
Patch Changes
@objectstack/plugin-email@9.10.0
Patch Changes
@objectstack/plugin-hono-server@9.10.0
Patch Changes
@objectstack/plugin-msw@9.10.0
Patch Changes
@objectstack/plugin-reports@9.10.0
Patch Changes
@objectstack/plugin-sharing@9.10.0
Patch Changes
@objectstack/plugin-webhooks@9.10.0
Patch Changes
@objectstack/rest@9.10.0
Patch Changes
fd07027: fix(analytics): make organization timezone actually drive date-dimension bucketing (ADR-0053 Phase 2, ADR-0053 Phase 2 · Slice 5: timezone-aware analytics date bucketing #1982)
Date-bucketed analytics silently ignored the reference timezone end-to-end. Three independent seams were broken:
NativeSQLStrategy(priority 10) won every cube/dataset query on a SQL driver, but it groups by the raw column (nodate_trunc) and ignorestimezone, so a date dimension never bucketed (one row per raw timestamp) and a non-UTC zone was dropped. It now declines queries that carry atimeDimensions[].granularity, handing them toObjectQLStrategy→engine.aggregate(native bucketing when UTC-safe, uniform in-memory bucketing when non-UTC).countaggregation treated the*count-all sentinel (the Cubecountmeasure / a fieldless datasetcount, both compiled tosql: '*') as a column name, counting non-null of a non-existent property →0for every bucket. The driver'sCOUNT(*)masked it; the in-memory path (non-UTC date buckets,driver-rest/driver-memory) returned zeros.*is now counted as all rows.resolveExecCtxnever resolved the localization timezone/locale, so/analytics/dataset/queryalways ran withtimezone: 'UTC'. It now resolves them through thesettingsservice (honouring the 4-tier cascade incl. theOS_LOCALIZATION_TIMEZONEenv override), mirroring the dispatcher path.Updated dependencies [db02bd5]
Updated dependencies [641675d]
Updated dependencies [94e9040]
Updated dependencies [1f88fd9]
Updated dependencies [1f88fd9]
@objectstack/service-ai@9.10.0
Patch Changes
@objectstack/service-analytics@9.10.0
Patch Changes
db02bd5: Fix dashboard time-series charts / "last N months" KPIs that filter or group by a
Field.datetimecolumn silently returning "No rows".The analytics
NativeSQLStrategycompiles dashboard relative-date tokens ({12_months_ago},{today}, …) to ISO date strings and binds them directly into raw SQL, bypassing the driver's own filter coercion. Under better-sqlite3 aField.datetimecolumn is stored as an INTEGER epoch (ms), soassessed_at >= '2025-06-18'became a TEXT-vs-INTEGER affinity compare that is always false — an empty result even though the rows exist.Field.datecolumns store ISO TEXT and were unaffected.The strategy now coerces a temporal comparand to the column's on-disk storage form via a new optional
StrategyContext.coerceTemporalFilterValuehook, wired to the driver's publicSqlDriver.temporalFilterValue(the single source of truth for the storage convention). Coercion is dialect-correct: SQLiteField.datetime→ epoch ms;Field.datetext and native-timestamp dialects (Postgres/MySQL) are left unchanged, so Postgres is never handed an epoch integer. Applied togte/lte/gt/lt/equals,in/notIn, and thedateRange/timeDimensionBETWEENpath.fd07027: fix(analytics): make organization timezone actually drive date-dimension bucketing (ADR-0053 Phase 2, ADR-0053 Phase 2 · Slice 5: timezone-aware analytics date bucketing #1982)
Date-bucketed analytics silently ignored the reference timezone end-to-end. Three independent seams were broken:
NativeSQLStrategy(priority 10) won every cube/dataset query on a SQL driver, but it groups by the raw column (nodate_trunc) and ignorestimezone, so a date dimension never bucketed (one row per raw timestamp) and a non-UTC zone was dropped. It now declines queries that carry atimeDimensions[].granularity, handing them toObjectQLStrategy→engine.aggregate(native bucketing when UTC-safe, uniform in-memory bucketing when non-UTC).countaggregation treated the*count-all sentinel (the Cubecountmeasure / a fieldless datasetcount, both compiled tosql: '*') as a column name, counting non-null of a non-existent property →0for every bucket. The driver'sCOUNT(*)masked it; the in-memory path (non-UTC date buckets,driver-rest/driver-memory) returned zeros.*is now counted as all rows.resolveExecCtxnever resolved the localization timezone/locale, so/analytics/dataset/queryalways ran withtimezone: 'UTC'. It now resolves them through thesettingsservice (honouring the 4-tier cascade incl. theOS_LOCALIZATION_TIMEZONEenv override), mirroring the dispatcher path.Updated dependencies [db02bd5]
Updated dependencies [641675d]
Updated dependencies [94e9040]
Updated dependencies [1f88fd9]
Updated dependencies [1f88fd9]
@objectstack/service-automation@9.10.0
Patch Changes
@objectstack/service-cache@9.10.0
Patch Changes
@objectstack/service-cluster@9.10.0
Patch Changes
@objectstack/service-cluster-redis@9.10.0
Patch Changes
@objectstack/service-datasource@9.10.0
Patch Changes
@objectstack/service-i18n@9.10.0
Patch Changes
@objectstack/service-job@9.10.0
Patch Changes
@objectstack/service-knowledge@9.10.0
Patch Changes
@objectstack/service-messaging@9.10.0
Patch Changes
@objectstack/service-package@9.10.0
Patch Changes
@objectstack/service-queue@9.10.0
Patch Changes
@objectstack/service-realtime@9.10.0
Patch Changes
@objectstack/service-settings@9.10.0
Patch Changes
@objectstack/service-storage@9.10.0
Patch Changes
@objectstack/trigger-api@9.10.0
Patch Changes
@objectstack/trigger-record-change@9.10.0
Patch Changes
@objectstack/trigger-schedule@9.10.0
Patch Changes
@objectstack/types@9.10.0
Patch Changes
@objectstack/console@9.10.0
create-objectstack@9.10.0
objectstack-vscode@9.10.0
@objectstack/example-crm@4.0.53
Patch Changes
@objectstack/example-showcase@0.1.23
Patch Changes
@objectstack/example-todo@4.0.53
Patch Changes
@objectstack/dogfood@0.0.1
Patch Changes