Skip to content
13 changes: 13 additions & 0 deletions .changeset/20336-number-comparand-declared-type-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@objectstack/spec': minor
---

`@objectstack/spec/data` declares the platform's numeric grammar for a string, and the contract of the number-comparand declared-type door: which string comparands a field whose declared type is numeric may be compared against (#20336).

Clause-②: yes

**The grammar.** A string is numeric when its whole content is a JSON number literal (`-?(0|[1-9][0-9]*)(.[0-9]+)?([eE][+-]?[0-9]+)?`) that names a finite number; it then means what the same characters mean as a JSON number. `NUMERIC_STRING_PATTERN`, `parseNumericString(s)` (the number, or `undefined`) and `readNumericString(s)` (the number, or which of eight named forms the string is: `empty`, `padded`, `placeholder`, `radix-prefix`, `non-finite`, `digit-separator`, `non-json-spelling`, `not-a-number`). `NUMERIC_STRING_GRAMMAR_CASES` records every form with the reason it is admitted or refused: `"12"`, `"-3"`, `"12.5"`, `"1e3"` and every string `String(n)` produces for a finite number are admitted; `""`, `" 12 "`, `"0x10"`, `"Infinity"`, `"NaN"`, `"1,000"`, `"+5"`, `".5"`, `"007"` and any `{placeholder}` are not.

**The door's contract.** `numberComparandDoorVerdict(field, comparand)` answers, for a comparand at a value position (implicit equality, `$eq` / `$ne` / `$gt` / `$gte` / `$lt` / `$lte`, and each member of `$in` / `$nin` / `$between`) of a filter on a field whose declared type is in `NUMERIC_VALUE_TYPES` (or a `formula` whose `returnType` is `number`): `door-refusal` (`INVALID_FILTER` / 400) for a non-numeric string, `narrows` with the number for a numeric one, `passes` for any other comparand or field, `deferred` for a `formula` without a readable `returnType`. `numberComparandRefusalMessage(site, context?)` is the refusal the door prints: the field, its declared type, the comparand, its position and what is wrong with it. A fixture object and a derived case table (`NUMBER_COMPARAND_DOOR_FIXTURE`, `NUMBER_COMPARAND_DOOR_CASES`) are published for the engine suite that pins the door.

**What moves for consumers.** Nothing yet. This is the contract only, and no door reads it in this release: a non-numeric string compared with a number field still reaches the driver as written (a 500 on PostgreSQL, an empty or different result elsewhere). The engine door that refuses it with `INVALID_FILTER` / 400 and narrows a numeric string lands with #20351, and the record validator's number arm adopts the same grammar for writes with #20309.
27 changes: 27 additions & 0 deletions packages/spec/api-surface/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -438,11 +438,21 @@
"MysqlConfig (type)",
"MysqlConfigParsed (type)",
"MysqlConfigSchema (const)",
"NON_NUMERIC_STRING_FORMS (const)",
"NON_TEXT_STORED_VALUE_TYPES (const)",
"NOW_DEFAULT_LEGAL_TYPES (const)",
"NUMBER_COMPARAND_DOOR_CASES (const)",
"NUMBER_COMPARAND_DOOR_FIXTURE (const)",
"NUMBER_COMPARAND_DOOR_FIXTURE_FIELDS (const)",
"NUMBER_COMPARAND_DOOR_FIXTURE_OBJECT (const)",
"NUMBER_COMPARAND_DOOR_JUDGED_TYPES (const)",
"NUMBER_COMPARAND_DOOR_LIST_OPERATORS (const)",
"NUMBER_COMPARAND_DOOR_SCALAR_OPERATORS (const)",
"NUMERIC_COLUMN_PRECISION (const)",
"NUMERIC_COLUMN_REPRESENTATION (const)",
"NUMERIC_COLUMN_SCALE (const)",
"NUMERIC_STRING_GRAMMAR_CASES (const)",
"NUMERIC_STRING_PATTERN (const)",
"NUMERIC_VALUE_TYPES (const)",
"NoSQLDataTypeMapping (type)",
"NoSQLDataTypeMappingSchema (const)",
Expand All @@ -462,9 +472,21 @@
"NoSQLQueryOptionsSchema (const)",
"NoSQLTransactionOptions (type)",
"NoSQLTransactionOptionsSchema (const)",
"NonNumericStringForm (type)",
"NormalizedFilter (type)",
"NormalizedFilterSchema (const)",
"NumberComparandDoorCase (type)",
"NumberComparandDoorDeferredCase (interface)",
"NumberComparandDoorFieldMeta (interface)",
"NumberComparandDoorFixtureField (interface)",
"NumberComparandDoorNarrowsCase (interface)",
"NumberComparandDoorPassesCase (interface)",
"NumberComparandDoorRefusalCase (interface)",
"NumberComparandDoorVerdict (type)",
"NumberComparandRefusalSite (interface)",
"NumericColumnRepresentation (type)",
"NumericStringGrammarCase (type)",
"NumericStringReading (type)",
"OBJECT_KEY_GUIDANCE (const)",
"OWNER_FIELD_DEF (const)",
"OWNING_BUSINESS_UNIT_FIELD_DEF (const)",
Expand Down Expand Up @@ -831,19 +853,24 @@
"missingFieldValues (function)",
"nextUtcCalendarDay (function)",
"normalizeFilterComparandTypes (function)",
"numberComparandDoorVerdict (function)",
"numberComparandFieldVerdict (function)",
"numberComparandRefusalMessage (function)",
"numericColumnFor (function)",
"objectForm (const)",
"objectTitleCompleteness (function)",
"parseAutonumberFormat (function)",
"parseDateMacroParam (function)",
"parseFilterAST (function)",
"parseNumericString (function)",
"passthroughSecretPaths (function)",
"percentScaleOf (function)",
"placeholderFree (function)",
"placeholderFreeDeep (function)",
"platformProvisionsStorage (function)",
"provisionPrimary (function)",
"readAutonumberCounter (function)",
"readNumericString (function)",
"redactDatasourceConfig (function)",
"redactUrlCredentialQueryParams (function)",
"redactUrlCredentials (function)",
Expand Down
27 changes: 27 additions & 0 deletions packages/spec/export-origins/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -428,11 +428,21 @@
"MysqlConfig": "src/data/driver/mysql.zod.ts#MysqlConfig (type)",
"MysqlConfigParsed": "src/data/driver/mysql.zod.ts#MysqlConfigParsed (type)",
"MysqlConfigSchema": "src/data/driver/mysql.zod.ts#MysqlConfigSchema (const)",
"NON_NUMERIC_STRING_FORMS": "src/data/filter-number-comparand-declared-type.ts#NON_NUMERIC_STRING_FORMS (const)",
"NON_TEXT_STORED_VALUE_TYPES": "src/data/field-value.zod.ts#NON_TEXT_STORED_VALUE_TYPES (const)",
"NOW_DEFAULT_LEGAL_TYPES": "src/data/default-value-shape.ts#NOW_DEFAULT_LEGAL_TYPES (const)",
"NUMBER_COMPARAND_DOOR_CASES": "src/data/filter-number-comparand-declared-type.ts#NUMBER_COMPARAND_DOOR_CASES (const)",
"NUMBER_COMPARAND_DOOR_FIXTURE": "src/data/filter-number-comparand-declared-type.ts#NUMBER_COMPARAND_DOOR_FIXTURE (const)",
"NUMBER_COMPARAND_DOOR_FIXTURE_FIELDS": "src/data/filter-number-comparand-declared-type.ts#NUMBER_COMPARAND_DOOR_FIXTURE_FIELDS (const)",
"NUMBER_COMPARAND_DOOR_FIXTURE_OBJECT": "src/data/filter-number-comparand-declared-type.ts#NUMBER_COMPARAND_DOOR_FIXTURE_OBJECT (const)",
"NUMBER_COMPARAND_DOOR_JUDGED_TYPES": "src/data/filter-number-comparand-declared-type.ts#NUMBER_COMPARAND_DOOR_JUDGED_TYPES (const)",
"NUMBER_COMPARAND_DOOR_LIST_OPERATORS": "src/data/filter-number-comparand-declared-type.ts#NUMBER_COMPARAND_DOOR_LIST_OPERATORS (const)",
"NUMBER_COMPARAND_DOOR_SCALAR_OPERATORS": "src/data/filter-number-comparand-declared-type.ts#NUMBER_COMPARAND_DOOR_SCALAR_OPERATORS (const)",
"NUMERIC_COLUMN_PRECISION": "src/data/numeric-column-representation.ts#NUMERIC_COLUMN_PRECISION (const)",
"NUMERIC_COLUMN_REPRESENTATION": "src/data/numeric-column-representation.ts#NUMERIC_COLUMN_REPRESENTATION (const)",
"NUMERIC_COLUMN_SCALE": "src/data/numeric-column-representation.ts#NUMERIC_COLUMN_SCALE (const)",
"NUMERIC_STRING_GRAMMAR_CASES": "src/data/filter-number-comparand-declared-type.ts#NUMERIC_STRING_GRAMMAR_CASES (const)",
"NUMERIC_STRING_PATTERN": "src/data/filter-number-comparand-declared-type.ts#NUMERIC_STRING_PATTERN (const)",
"NUMERIC_VALUE_TYPES": "src/data/field-value.zod.ts#NUMERIC_VALUE_TYPES (const)",
"NoSQLDataTypeMapping": "src/data/driver-nosql.zod.ts#NoSQLDataTypeMapping (type)",
"NoSQLDataTypeMappingSchema": "src/data/driver-nosql.zod.ts#NoSQLDataTypeMappingSchema (const)",
Expand All @@ -452,9 +462,21 @@
"NoSQLQueryOptionsSchema": "src/data/driver-nosql.zod.ts#NoSQLQueryOptionsSchema (const)",
"NoSQLTransactionOptions": "src/data/driver-nosql.zod.ts#NoSQLTransactionOptions (type)",
"NoSQLTransactionOptionsSchema": "src/data/driver-nosql.zod.ts#NoSQLTransactionOptionsSchema (const)",
"NonNumericStringForm": "src/data/filter-number-comparand-declared-type.ts#NonNumericStringForm (type)",
"NormalizedFilter": "src/data/filter.zod.ts#NormalizedFilter (type)",
"NormalizedFilterSchema": "src/data/filter.zod.ts#NormalizedFilterSchema (const)",
"NumberComparandDoorCase": "src/data/filter-number-comparand-declared-type.ts#NumberComparandDoorCase (type)",
"NumberComparandDoorDeferredCase": "src/data/filter-number-comparand-declared-type.ts#NumberComparandDoorDeferredCase (interface)",
"NumberComparandDoorFieldMeta": "src/data/filter-number-comparand-declared-type.ts#NumberComparandDoorFieldMeta (interface)",
"NumberComparandDoorFixtureField": "src/data/filter-number-comparand-declared-type.ts#NumberComparandDoorFixtureField (interface)",
"NumberComparandDoorNarrowsCase": "src/data/filter-number-comparand-declared-type.ts#NumberComparandDoorNarrowsCase (interface)",
"NumberComparandDoorPassesCase": "src/data/filter-number-comparand-declared-type.ts#NumberComparandDoorPassesCase (interface)",
"NumberComparandDoorRefusalCase": "src/data/filter-number-comparand-declared-type.ts#NumberComparandDoorRefusalCase (interface)",
"NumberComparandDoorVerdict": "src/data/filter-number-comparand-declared-type.ts#NumberComparandDoorVerdict (type)",
"NumberComparandRefusalSite": "src/data/filter-number-comparand-declared-type.ts#NumberComparandRefusalSite (interface)",
"NumericColumnRepresentation": "src/data/numeric-column-representation.ts#NumericColumnRepresentation (type)",
"NumericStringGrammarCase": "src/data/filter-number-comparand-declared-type.ts#NumericStringGrammarCase (type)",
"NumericStringReading": "src/data/filter-number-comparand-declared-type.ts#NumericStringReading (type)",
"OBJECT_KEY_GUIDANCE": "src/data/authoring-key-lint.ts#OBJECT_KEY_GUIDANCE (const)",
"OWNER_FIELD_DEF": "src/data/injected-system-column-provenance.ts#OWNER_FIELD_DEF (const)",
"OWNING_BUSINESS_UNIT_FIELD_DEF": "src/data/injected-system-column-provenance.ts#OWNING_BUSINESS_UNIT_FIELD_DEF (const)",
Expand Down Expand Up @@ -818,19 +840,24 @@
"missingFieldValues": "src/data/autonumber-format.ts#missingFieldValues (function)",
"nextUtcCalendarDay": "src/data/calendar-day.ts#nextUtcCalendarDay (function)",
"normalizeFilterComparandTypes": "src/data/filter-comparand-type.ts#normalizeFilterComparandTypes (function)",
"numberComparandDoorVerdict": "src/data/filter-number-comparand-declared-type.ts#numberComparandDoorVerdict (function)",
"numberComparandFieldVerdict": "src/data/filter-number-comparand-declared-type.ts#numberComparandFieldVerdict (function)",
"numberComparandRefusalMessage": "src/data/filter-number-comparand-declared-type.ts#numberComparandRefusalMessage (function)",
"numericColumnFor": "src/data/numeric-column-representation.ts#numericColumnFor (function)",
"objectForm": "src/data/object.form.ts#objectForm (const)",
"objectTitleCompleteness": "src/data/display-name.ts#objectTitleCompleteness (function)",
"parseAutonumberFormat": "src/data/autonumber-format.ts#parseAutonumberFormat (function)",
"parseDateMacroParam": "src/data/date-macros.zod.ts#parseDateMacroParam (function)",
"parseFilterAST": "src/data/filter.zod.ts#parseFilterAST (function)",
"parseNumericString": "src/data/filter-number-comparand-declared-type.ts#parseNumericString (function)",
"passthroughSecretPaths": "src/data/datasource-credential-redaction.ts#passthroughSecretPaths (function)",
"percentScaleOf": "src/data/percent-scale.ts#percentScaleOf (function)",
"placeholderFree": "src/data/driver/common.zod.ts#placeholderFree (function)",
"placeholderFreeDeep": "src/data/driver/common.zod.ts#placeholderFreeDeep (function)",
"platformProvisionsStorage": "src/data/injected-system-column-provenance.ts#platformProvisionsStorage (function)",
"provisionPrimary": "src/data/display-name.ts#provisionPrimary (function)",
"readAutonumberCounter": "src/data/autonumber-format.ts#readAutonumberCounter (function)",
"readNumericString": "src/data/filter-number-comparand-declared-type.ts#readNumericString (function)",
"redactDatasourceConfig": "src/data/datasource-credential-redaction.ts#redactDatasourceConfig (function)",
"redactUrlCredentialQueryParams": "src/data/datasource-credential-redaction.ts#redactUrlCredentialQueryParams (function)",
"redactUrlCredentials": "src/data/datasource-credential-redaction.ts#redactUrlCredentials (function)",
Expand Down
Loading
Loading