@aglyn/shared-ui-jsx 1.0.0-beta.206 → 1.0.0-beta.209
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/shared-ui-jsx",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.209",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
29
|
-
"@aglyn/shared-ui-theme": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-util-tools": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-util-vendor": "1.0.0-beta.
|
|
28
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.209",
|
|
29
|
+
"@aglyn/shared-ui-theme": "1.0.0-beta.209",
|
|
30
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.209",
|
|
31
|
+
"@aglyn/shared-util-vendor": "1.0.0-beta.209",
|
|
32
32
|
"@mdi/js": "^7.4.47",
|
|
33
33
|
"@swc/helpers": "0.5.23",
|
|
34
34
|
"change-case": "^5.4.4",
|
|
@@ -503,7 +503,10 @@ const disjunctionsOf = (filter)=>filter.op === 'in' || filter.op === 'array-cont
|
|
|
503
503
|
var _request_sort, _request_sort1;
|
|
504
504
|
return sort.path === ((_request_sort = request.sort) == null ? void 0 : _request_sort.path) && sort.direction === ((_request_sort1 = request.sort) == null ? void 0 : _request_sort1.direction);
|
|
505
505
|
}) : undefined;
|
|
506
|
-
|
|
506
|
+
// A range leads the order with the field it ranges over — in the asked
|
|
507
|
+
// direction when the asked order IS that field, since a declared order has
|
|
508
|
+
// its composites either way.
|
|
509
|
+
const orderBy = inequality ? (asked == null ? void 0 : asked.path) === inequality ? asked : rangeOrder(declaration, declaration.fields.find((field)=>field.path === inequality || field.lowerPath === inequality || field.reversedPath === inequality), inequality) : (_ref1 = asked != null ? asked : declaration.sorts[0]) != null ? _ref1 : {
|
|
507
510
|
path: LIST_QUERY_ID_PATH,
|
|
508
511
|
direction: 'asc'
|
|
509
512
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/shared/ui/jsx/src/lib/const/list-query-plan.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n type ListFilterField,\n type ListFilterRequest,\n listFilterDay,\n listFilterOperators,\n} from './list-filter'\n\n/*\n * EVERY CLAUSE ON THE QUERY (AGL-3321).\n *\n * The AGL-2501 contract turned ONE filter into a Firestore query — the list\n * put the first servable clause on its query and matched the rest over the\n * rows it had loaded, and search the same way. A clause matched over a loaded\n * page answers \"no match\" for a record on the next one, which is the one\n * answer a list must never give wrongly.\n *\n * This composes them all. A list declares its fields (the same\n * `ListFilterField` grammar), the sorts it offers and the token array its\n * quick search reads; `planListQuery` turns the clauses in force and the\n * search words into ONE query — every predicate, one order — and says which\n * it could not put there and why. Nothing is matched in memory afterwards:\n * a clause the plan refuses is not applied, and the list says so, rather\n * than being applied to some rows and not others.\n *\n * The plan is SDK-free and model-free: the text normalizers the written\n * search fields were stamped with come in as `ListQueryNormalizers` — the\n * platform's are `nameSearchNormalizers` (`@aglyn/aglyn/app-utils/name-search`)\n * — so the query asks for exactly the keys the writers stored. The web twin (`listQueryConstraints` in the instance\n * library) and the Admin twin (`applyListQuery` in the console) turn it into\n * their SDK's query, so the two cannot disagree about what a clause means.\n *\n * ## What one Firestore query can hold\n *\n * - any number of equalities (`==`, `in`), composed through INDEX MERGING:\n * one `(field, sort)` composite per equality field serves every\n * combination of them, measured against production (AGL-3321);\n * - ONE array clause — `array-contains` or `array-contains-any` — which a\n * search token, a `contains` filter and a scope clause all compete for;\n * - ONE inequality field (a range, `!=`, a prefix range), which must then\n * lead the order — so a range orders the list by the field it ranges over;\n * - at most thirty disjunctions, which an `in` and an\n * `array-contains-any` multiply.\n *\n * The declaration's `listQueryIndexes` enumerates the composites those\n * shapes need, and each list's spec holds the index file to it.\n */\n\n/**\n * How the written search fields were normalized, so a typed value becomes the\n * key the query can match: the lower-cased key (`equals`, `startsWith`), the\n * one word-prefix token (`contains`, search), and the reversed key\n * (`endsWith`). `maxPrefix` is the longest token a writer stores.\n */\nexport interface ListQueryNormalizers {\n key(value: string): string\n token(value: string): string\n reversed(value: string): string\n maxPrefix: number\n}\n\n/** A predicate on the query, SDK-free. `path` `__name__` is the document id. */\nexport type ListQueryOp =\n | '=='\n | '!='\n | '<'\n | '<='\n | '>'\n | '>='\n | 'in'\n | 'array-contains'\n | 'array-contains-any'\n\nexport type ListQueryValue = string | number | boolean | null | Date | readonly (string | number)[]\n\nexport interface ListQueryFilter {\n path: string\n op: ListQueryOp\n value: ListQueryValue\n}\n\nexport interface ListQuerySort {\n path: string\n direction: 'asc' | 'desc'\n /** The grid column this order sorts, when the list lets the grid sort by it. */\n column?: string\n}\n\n/** The document id, as a path. */\nexport const LIST_QUERY_ID_PATH = '__name__'\n\n/** Firestore's cap on disjunctions — `in` values times `array-contains-any` values. */\nexport const LIST_QUERY_DISJUNCTIONS = 30\n\n/** A very high private-use codepoint: `[p, p + HIGH]` spans every string starting `p`. */\nconst HIGH = '\\uf8ff'\n\nexport interface ListQueryDeclaration {\n /** Every field the Filters panel may name. */\n fields: readonly ListFilterField[]\n /**\n * The orders the list offers, its default FIRST. The query takes no other:\n * each is a composite the index file carries for every filterable field.\n */\n sorts: readonly ListQuerySort[]\n /**\n * The quick search: the token array every write stamps (see\n * `nameSearchTokens`), queried with `array-contains` on one typed word.\n * Absent, the list offers no search box.\n */\n search?: {\n tokensPath: string\n /**\n * Under a scope clause the search cannot be a SECOND array clause, so a\n * scoped list writes `<scopeToken><join><prefix>` tokens and the query\n * asks `array-contains-any` over the scope's tokens joined to the word —\n * one array clause that answers both.\n *\n * ⛔ Rules that grant a list from `visibleTo.hasAny(...)` DENY this\n * query, because it no longer constrains `visibleTo`. Declare it only for\n * readers whose rule needs no `visibleTo` term, or for an Admin-SDK read;\n * see `SCOPED_SEARCH_JOIN` in `@aglyn/aglyn/app-utils/name-search`.\n */\n scoped?: { tokensPath: string; join: string }\n }\n /**\n * Keys a presence-map field (`keysOf`) is filtered by, for the index\n * enumeration: `sources.form`, `sources.order`, …\n */\n keys?: Readonly<Record<string, readonly string[]>>\n}\n\n/** What the list asks this time. */\nexport interface ListQueryRequest {\n clauses: readonly ListFilterRequest[]\n /** The quick search's words. */\n search?: readonly string[]\n /** The order asked for; a sort the declaration does not offer is ignored. */\n sort?: ListQuerySort | null\n /**\n * Predicates the list always applies — its scope — which count against\n * the same limits. An `array-contains-any` scope is what a scoped search\n * folds into (`declaration.search.scoped`).\n */\n base?: readonly ListQueryFilter[]\n}\n\nexport interface ListQueryRefusal {\n /** The clause refused, or `search`. */\n clause: ListFilterRequest | 'search'\n reason: string\n}\n\nexport interface ListQueryPlan {\n /** Every predicate, base included, in the order the SDK should add them. */\n filters: ListQueryFilter[]\n /** The one order: the range field's when there is one, else the sort. */\n orderBy: ListQuerySort\n /** The clauses on the query. */\n served: ListFilterRequest[]\n /** The search word on the query, or null. */\n searched: string | null\n /** What was asked and could not be put on the query, with why. */\n refused: ListQueryRefusal[]\n /** Said to the reader about what WAS served — e.g. one search word of two. */\n notices: string[]\n}\n\n/** How one clause lands on a query, before it is composed with the others. */\ninterface ClauseShape {\n filters: ListQueryFilter[]\n /** The inequality field the clause needs to lead the order, if any. */\n inequality?: string\n /** The clause is the query's array clause. */\n array?: boolean\n /** `in` values, for the disjunction budget. */\n disjunctions?: number\n}\n\nconst csv = (raw: string): string[] =>\n raw\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean)\n\nconst dayBounds = (raw: string): { start: Date; end: Date } | null => {\n const start = listFilterDay(raw)\n if (Number.isNaN(start.getTime())) return null\n const day = new Date(start)\n day.setHours(0, 0, 0, 0)\n const end = new Date(day)\n end.setDate(end.getDate() + 1)\n return { start: day, end }\n}\n\n/** The order a range over `path` puts the list in. */\nconst rangeOrder = (\n declaration: ListQueryDeclaration,\n field: ListFilterField | undefined,\n path: string,\n): ListQuerySort => {\n const offered = declaration.sorts.find((sort) => sort.path === path)\n if (offered) return offered\n return { path, direction: field?.kind === 'date' ? 'desc' : 'asc' }\n}\n\n/**\n * One clause as predicates, or the reason it has none. The same operator\n * meanings as the AGL-2501 twins (`applyListFilter`, `listFilterConstraints`),\n * which this replaces for a list that composes clauses.\n */\nfunction shapeClause(\n field: ListFilterField,\n input: ListFilterRequest,\n names: ListQueryNormalizers,\n): ClauseShape | string {\n if (field.windowOnly) return 'this field is not stored where a query can reach it'\n if (!listFilterOperators(field).includes(input.op)) {\n return `${input.op} is not something this list can ask of ${field.column}`\n }\n const raw = (input.value ?? '').trim()\n const op = input.op\n\n if (op === 'isEmpty') {\n if (field.presence !== 'nullable') return 'only a field stored as null can be asked for empty'\n return { filters: [{ path: field.path, op: '==', value: null }] }\n }\n if (op === 'isNotEmpty') {\n if (field.presence === 'always') return 'this field is never empty'\n return { filters: [{ path: field.path, op: '!=', value: null }], inequality: field.path }\n }\n if (field.kind === 'boolean') {\n if (raw !== 'true' && raw !== 'false') return 'pick true or false'\n return { filters: [{ path: field.path, op: '==', value: raw === 'true' }] }\n }\n if (!raw) return 'no value yet'\n\n if (field.keysOf) {\n if (op !== 'equals' || !/^[A-Za-z0-9_]+$/.test(raw)) return 'pick one of the choices'\n return { filters: [{ path: `${field.path}.${raw}`, op: '==', value: true }] }\n }\n\n if (field.kind === 'text') {\n if (op === 'contains' && field.tokensPath) {\n const token = field.verbatimTokens ? raw : names.token(raw)\n if (!token) return 'no value yet'\n return {\n filters: [{ path: field.tokensPath, op: 'array-contains', value: token }],\n array: true,\n }\n }\n if (op === 'equals' && field.lowerPath) {\n return { filters: [{ path: field.lowerPath, op: '==', value: names.key(raw) }] }\n }\n if (op === 'equals' && field.presence === 'always' && !field.lowerPath) {\n return { filters: [{ path: field.path, op: '==', value: raw }] }\n }\n if (op === 'startsWith' && field.lowerPath) {\n const key = names.key(raw)\n return {\n filters: [\n { path: field.lowerPath, op: '>=', value: key },\n { path: field.lowerPath, op: '<=', value: `${key}${HIGH}` },\n ],\n inequality: field.lowerPath,\n }\n }\n if (op === 'endsWith' && field.reversedPath) {\n const key = names.reversed(raw)\n return {\n filters: [\n { path: field.reversedPath, op: '>=', value: key },\n { path: field.reversedPath, op: '<=', value: `${key}${HIGH}` },\n ],\n inequality: field.reversedPath,\n }\n }\n if (op === 'isAnyOf' && field.lowerPath) {\n const values = csv(raw).map((value) => names.key(value))\n if (values.length > LIST_QUERY_DISJUNCTIONS) return `at most ${LIST_QUERY_DISJUNCTIONS} values`\n return {\n filters: [{ path: field.lowerPath, op: 'in', value: values }],\n disjunctions: values.length,\n }\n }\n return `${op} is not something this list can ask of ${field.column}`\n }\n\n if (field.kind === 'id') {\n if (op === 'equals') return { filters: [{ path: LIST_QUERY_ID_PATH, op: '==', value: raw }] }\n if (op === 'isAnyOf') {\n const values = csv(raw)\n if (values.length > LIST_QUERY_DISJUNCTIONS) return `at most ${LIST_QUERY_DISJUNCTIONS} values`\n return {\n filters: [{ path: LIST_QUERY_ID_PATH, op: 'in', value: values }],\n disjunctions: values.length,\n }\n }\n if (op === 'startsWith') {\n return {\n filters: [\n { path: LIST_QUERY_ID_PATH, op: '>=', value: raw },\n { path: LIST_QUERY_ID_PATH, op: '<=', value: `${raw}${HIGH}` },\n ],\n inequality: LIST_QUERY_ID_PATH,\n }\n }\n return `${op} is not something this list can ask of ${field.column}`\n }\n\n if (field.kind === 'exact') {\n if (op === 'equals') return { filters: [{ path: field.path, op: '==', value: raw }] }\n if (op === 'isAnyOf') {\n const values = csv(raw)\n if (values.length > LIST_QUERY_DISJUNCTIONS) return `at most ${LIST_QUERY_DISJUNCTIONS} values`\n if (field.tokensPath) {\n // An array field asked \"any of these\": one array clause.\n return {\n filters: [{ path: field.tokensPath, op: 'array-contains-any', value: values }],\n array: true,\n disjunctions: values.length,\n }\n }\n return {\n filters: [{ path: field.path, op: 'in', value: values }],\n disjunctions: values.length,\n }\n }\n if (op === 'contains' && field.tokensPath) {\n return {\n filters: [{ path: field.tokensPath, op: 'array-contains', value: raw }],\n array: true,\n }\n }\n return `${op} is not something this list can ask of ${field.column}`\n }\n\n if (field.kind === 'number') {\n const value = Number(raw)\n if (!Number.isFinite(value)) return 'type a number'\n if (op === '=') return { filters: [{ path: field.path, op: '==', value }] }\n const comparison: Record<string, ListQueryOp> = {\n '!=': '!=',\n '>': '>',\n '>=': '>=',\n '<': '<',\n '<=': '<=',\n }\n const found = comparison[op]\n if (!found) return `${op} is not something this list can ask of ${field.column}`\n return { filters: [{ path: field.path, op: found, value }], inequality: field.path }\n }\n\n if (field.kind === 'date') {\n const day = dayBounds(raw)\n if (!day) return 'pick a date'\n // A `…AtMs` field is compared as the number it is stored as.\n const at = (date: Date): Date | number =>\n field.storedAs === 'millis' ? date.getTime() : date\n if (op === 'is') {\n return {\n filters: [\n { path: field.path, op: '>=', value: at(day.start) },\n { path: field.path, op: '<', value: at(day.end) },\n ],\n inequality: field.path,\n }\n }\n const bound: Record<string, [ListQueryOp, Date]> = {\n after: ['>=', day.end],\n onOrAfter: ['>=', day.start],\n before: ['<', day.start],\n onOrBefore: ['<', day.end],\n }\n const found = bound[op]\n if (!found) return `${op} is not something this list can ask of ${field.column}`\n return { filters: [{ path: field.path, op: found[0], value: at(found[1]) }], inequality: field.path }\n }\n\n return `${op} is not something this list can ask of ${field.column}`\n}\n\nconst disjunctionsOf = (filter: ListQueryFilter): number =>\n filter.op === 'in' || filter.op === 'array-contains-any'\n ? Math.max(1, Array.isArray(filter.value) ? filter.value.length : 1)\n : 1\n\n/**\n * Every clause and the search, as ONE query — or as much of it as one query\n * can hold, with the rest refused by name.\n *\n * Order of claim: the base (the list's scope) first, then the search, then\n * the clauses in the order they were added. A clause that would be a second\n * array clause, a second inequality field or push the disjunctions past\n * thirty is refused, and the list shows the refusal beside its chip; the\n * clauses before it stand. The search is claimed before the clauses because\n * it is the thing a reader typed last and looks at.\n */\nexport function planListQuery(\n declaration: ListQueryDeclaration,\n request: ListQueryRequest,\n names: ListQueryNormalizers,\n): ListQueryPlan {\n const filters: ListQueryFilter[] = [...(request.base ?? [])]\n const served: ListFilterRequest[] = []\n const refused: ListQueryRefusal[] = []\n const notices: string[] = []\n let arrayTaken = filters.some(\n (filter) => filter.op === 'array-contains' || filter.op === 'array-contains-any',\n )\n let inequality: string | null =\n filters.find((filter) => ['!=', '<', '<=', '>', '>='].includes(filter.op))?.path ?? null\n let disjunctions = filters.reduce((product, filter) => product * disjunctionsOf(filter), 1)\n\n // The search.\n let searched: string | null = null\n const words = (request.search ?? []).map((word) => word.trim()).filter(Boolean)\n if (words.length) {\n const search = declaration.search\n const token = names.token(words.join(' '))\n if (!search) {\n refused.push({ clause: 'search', reason: 'this list has no search' })\n } else if (!token) {\n // Nothing searchable was typed.\n } else {\n const scopeAt = filters.findIndex(\n (filter) => filter.op === 'array-contains-any' || filter.op === 'array-contains',\n )\n if (scopeAt === -1) {\n filters.push({ path: search.tokensPath, op: 'array-contains', value: token })\n arrayTaken = true\n searched = token\n } else if (search.scoped) {\n // Fold the search into the scope: one array clause answers both.\n const scope = filters[scopeAt]\n const scopes = Array.isArray(scope.value) ? scope.value : [scope.value]\n filters.splice(scopeAt, 1, {\n path: search.scoped.tokensPath,\n op: 'array-contains-any',\n value: scopes.map((entry) => `${String(entry)}${search.scoped?.join}${token}`),\n })\n searched = token\n } else {\n refused.push({\n clause: 'search',\n reason: 'this list is already narrowed to what you can see, which search cannot combine with',\n })\n }\n if (searched && words.length > 1) {\n notices.push(\n `Search matches one word at a time: showing results for \"${token}\".`,\n )\n }\n if (searched && names.key(words[0]).length > names.maxPrefix) {\n notices.push(`Search reads the first ${names.maxPrefix} letters of a word.`)\n }\n }\n }\n\n // The clauses.\n for (const clause of request.clauses) {\n const field = declaration.fields.find((entry) => entry.column === clause.field)\n if (!field) {\n refused.push({ clause, reason: 'this list does not filter by that' })\n continue\n }\n const shape = shapeClause(field, clause, names)\n if (typeof shape === 'string') {\n refused.push({ clause, reason: shape })\n continue\n }\n if (shape.array && arrayTaken) {\n refused.push({\n clause,\n reason: searched\n ? 'cannot be combined with the search — clear the search to use it'\n : 'cannot be combined with another \"contains\" or \"any of\" filter on a list',\n })\n continue\n }\n if (shape.inequality && inequality && shape.inequality !== inequality) {\n refused.push({\n clause,\n reason: 'only one range (dates, numbers, starts with) can apply at a time',\n })\n continue\n }\n const product =\n disjunctions * shape.filters.reduce((total, filter) => total * disjunctionsOf(filter), 1)\n if (product > LIST_QUERY_DISJUNCTIONS) {\n refused.push({ clause, reason: `too many values at once (the limit is ${LIST_QUERY_DISJUNCTIONS})` })\n continue\n }\n filters.push(...shape.filters)\n served.push(clause)\n if (shape.array) arrayTaken = true\n if (shape.inequality) inequality = shape.inequality\n disjunctions = product\n }\n\n // The order.\n const asked = request.sort\n ? declaration.sorts.find(\n (sort) => sort.path === request.sort?.path && sort.direction === request.sort?.direction,\n )\n : undefined\n const orderBy = inequality\n ? rangeOrder(\n declaration,\n declaration.fields.find(\n (field) =>\n field.path === inequality ||\n field.lowerPath === inequality ||\n field.reversedPath === inequality,\n ),\n inequality,\n )\n : (asked ?? declaration.sorts[0] ?? { path: LIST_QUERY_ID_PATH, direction: 'asc' })\n\n return { filters, orderBy, served, searched, refused, notices }\n}\n\n/** A composite the plan's shapes need, in the index file's own terms. */\nexport interface ListQueryIndex {\n fields: Array<{ fieldPath: string; order?: 'ASCENDING' | 'DESCENDING'; arrayConfig?: 'CONTAINS' }>\n}\n\nconst ORDER = { asc: 'ASCENDING', desc: 'DESCENDING' } as const\n\n/**\n * Every composite a declaration's queries can need, under index merging:\n * one `(predicate field, order)` index per equality or array field and per\n * order the list can take — its sorts, and the order each range imposes.\n *\n * `base` names the scope predicates the list always applies (their paths\n * and whether they are array clauses), which need their own index per order\n * like any other equality. Pinned by each list's spec against\n * `cloud/firebase-firestore.indexes.json`.\n */\nexport function listQueryIndexes(\n declaration: ListQueryDeclaration,\n base: readonly { path: string; array?: boolean }[] = [],\n): ListQueryIndex[] {\n const equalities = new Map<string, boolean>() // path → is array clause\n const orders: ListQuerySort[] = [...declaration.sorts]\n const addOrder = (sort: ListQuerySort) => {\n if (!orders.some((entry) => entry.path === sort.path && entry.direction === sort.direction)) {\n orders.push(sort)\n }\n }\n for (const entry of base) equalities.set(entry.path, Boolean(entry.array))\n if (declaration.search) {\n equalities.set(declaration.search.tokensPath, true)\n if (declaration.search.scoped) equalities.set(declaration.search.scoped.tokensPath, true)\n }\n for (const field of declaration.fields) {\n if (field.windowOnly) continue\n const ops = listFilterOperators(field)\n const has = (...names: string[]) => names.some((name) => ops.includes(name))\n if (field.keysOf) {\n for (const key of declaration.keys?.[field.column] ?? []) {\n equalities.set(`${field.path}.${key}`, false)\n }\n continue\n }\n if (field.kind === 'text') {\n if (has('contains') && field.tokensPath) equalities.set(field.tokensPath, true)\n if (has('equals', 'isAnyOf') && field.lowerPath) equalities.set(field.lowerPath, false)\n if (has('equals') && !field.lowerPath && field.presence === 'always') equalities.set(field.path, false)\n if (has('startsWith') && field.lowerPath) addOrder(rangeOrder(declaration, field, field.lowerPath))\n if (has('endsWith') && field.reversedPath) addOrder(rangeOrder(declaration, field, field.reversedPath))\n } else if (field.kind === 'exact') {\n if (has('equals', 'isAnyOf')) equalities.set(field.path, false)\n if (has('contains', 'isAnyOf') && field.tokensPath) equalities.set(field.tokensPath, true)\n } else if (field.kind === 'boolean') {\n equalities.set(field.path, false)\n } else if (field.kind === 'number') {\n if (has('=')) equalities.set(field.path, false)\n if (has('!=', '>', '>=', '<', '<=')) addOrder(rangeOrder(declaration, field, field.path))\n } else if (field.kind === 'date') {\n if (has('is', 'after', 'onOrAfter', 'before', 'onOrBefore')) {\n addOrder(rangeOrder(declaration, field, field.path))\n }\n }\n if (has('isEmpty')) equalities.set(field.path, false)\n if (has('isNotEmpty')) addOrder(rangeOrder(declaration, field, field.path))\n }\n const indexes: ListQueryIndex[] = []\n const seen = new Set<string>()\n for (const [path, array] of equalities) {\n for (const order of orders) {\n if (order.path === path || order.path === LIST_QUERY_ID_PATH) continue\n const index: ListQueryIndex = {\n fields: [\n array ? { fieldPath: path, arrayConfig: 'CONTAINS' } : { fieldPath: path, order: 'ASCENDING' },\n { fieldPath: order.path, order: ORDER[order.direction] },\n ],\n }\n const key = JSON.stringify(index)\n if (!seen.has(key)) {\n seen.add(key)\n indexes.push(index)\n }\n }\n }\n return indexes\n}\n\n/** An index entry as `cloud/firebase-firestore.indexes.json` stores it. */\ninterface IndexFileEntry {\n collectionGroup: string\n queryScope: string\n fields: Array<{ fieldPath: string; order?: string; arrayConfig?: string }>\n}\n\n/**\n * The composites a declaration needs that an index file does not hold — what\n * a list's spec asserts is empty, so a declared filter cannot ship without\n * the index that serves it.\n */\nexport function missingListQueryIndexes(\n file: { indexes: readonly IndexFileEntry[] },\n collectionGroup: string,\n needed: readonly ListQueryIndex[],\n queryScope: 'COLLECTION' | 'COLLECTION_GROUP' = 'COLLECTION',\n): ListQueryIndex[] {\n const shape = (fields: IndexFileEntry['fields']) =>\n fields\n .filter((field) => field.fieldPath !== LIST_QUERY_ID_PATH)\n .map((field) => `${field.fieldPath}:${field.order ?? field.arrayConfig}`)\n .join(',')\n const present = new Set(\n file.indexes\n .filter((index) => index.collectionGroup === collectionGroup && index.queryScope === queryScope)\n .map((index) => shape(index.fields)),\n )\n return needed.filter((index) => !present.has(shape(index.fields)))\n}\n"],"names":["listFilterDay","listFilterOperators","LIST_QUERY_ID_PATH","LIST_QUERY_DISJUNCTIONS","HIGH","csv","raw","split","map","entry","trim","filter","Boolean","dayBounds","start","Number","isNaN","getTime","day","Date","setHours","end","setDate","getDate","rangeOrder","declaration","field","path","offered","sorts","find","sort","direction","kind","shapeClause","input","names","windowOnly","includes","op","column","value","presence","filters","inequality","keysOf","test","tokensPath","token","verbatimTokens","array","lowerPath","key","reversedPath","reversed","values","length","disjunctions","isFinite","comparison","found","at","date","storedAs","bound","after","onOrAfter","before","onOrBefore","disjunctionsOf","Math","max","Array","isArray","planListQuery","request","asked","base","served","refused","notices","arrayTaken","some","reduce","product","searched","words","search","word","join","push","clause","reason","scopeAt","findIndex","scoped","scope","scopes","splice","String","maxPrefix","clauses","fields","shape","total","undefined","orderBy","ORDER","asc","desc","listQueryIndexes","equalities","Map","orders","addOrder","set","ops","has","name","keys","indexes","seen","Set","order","index","fieldPath","arrayConfig","JSON","stringify","add","missingListQueryIndexes","file","collectionGroup","needed","queryScope","present"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/shared/ui/jsx/src/lib/const/list-query-plan.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n type ListFilterField,\n type ListFilterRequest,\n listFilterDay,\n listFilterOperators,\n} from './list-filter'\n\n/*\n * EVERY CLAUSE ON THE QUERY (AGL-3321).\n *\n * The AGL-2501 contract turned ONE filter into a Firestore query — the list\n * put the first servable clause on its query and matched the rest over the\n * rows it had loaded, and search the same way. A clause matched over a loaded\n * page answers \"no match\" for a record on the next one, which is the one\n * answer a list must never give wrongly.\n *\n * This composes them all. A list declares its fields (the same\n * `ListFilterField` grammar), the sorts it offers and the token array its\n * quick search reads; `planListQuery` turns the clauses in force and the\n * search words into ONE query — every predicate, one order — and says which\n * it could not put there and why. Nothing is matched in memory afterwards:\n * a clause the plan refuses is not applied, and the list says so, rather\n * than being applied to some rows and not others.\n *\n * The plan is SDK-free and model-free: the text normalizers the written\n * search fields were stamped with come in as `ListQueryNormalizers` — the\n * platform's are `nameSearchNormalizers` (`@aglyn/aglyn/app-utils/name-search`)\n * — so the query asks for exactly the keys the writers stored. The web twin (`listQueryConstraints` in the instance\n * library) and the Admin twin (`applyListQuery` in the console) turn it into\n * their SDK's query, so the two cannot disagree about what a clause means.\n *\n * ## What one Firestore query can hold\n *\n * - any number of equalities (`==`, `in`), composed through INDEX MERGING:\n * one `(field, sort)` composite per equality field serves every\n * combination of them, measured against production (AGL-3321);\n * - ONE array clause — `array-contains` or `array-contains-any` — which a\n * search token, a `contains` filter and a scope clause all compete for;\n * - ONE inequality field (a range, `!=`, a prefix range), which must then\n * lead the order — so a range orders the list by the field it ranges over;\n * - at most thirty disjunctions, which an `in` and an\n * `array-contains-any` multiply.\n *\n * The declaration's `listQueryIndexes` enumerates the composites those\n * shapes need, and each list's spec holds the index file to it.\n */\n\n/**\n * How the written search fields were normalized, so a typed value becomes the\n * key the query can match: the lower-cased key (`equals`, `startsWith`), the\n * one word-prefix token (`contains`, search), and the reversed key\n * (`endsWith`). `maxPrefix` is the longest token a writer stores.\n */\nexport interface ListQueryNormalizers {\n key(value: string): string\n token(value: string): string\n reversed(value: string): string\n maxPrefix: number\n}\n\n/** A predicate on the query, SDK-free. `path` `__name__` is the document id. */\nexport type ListQueryOp =\n | '=='\n | '!='\n | '<'\n | '<='\n | '>'\n | '>='\n | 'in'\n | 'array-contains'\n | 'array-contains-any'\n\nexport type ListQueryValue = string | number | boolean | null | Date | readonly (string | number)[]\n\nexport interface ListQueryFilter {\n path: string\n op: ListQueryOp\n value: ListQueryValue\n}\n\nexport interface ListQuerySort {\n path: string\n direction: 'asc' | 'desc'\n /** The grid column this order sorts, when the list lets the grid sort by it. */\n column?: string\n}\n\n/** The document id, as a path. */\nexport const LIST_QUERY_ID_PATH = '__name__'\n\n/** Firestore's cap on disjunctions — `in` values times `array-contains-any` values. */\nexport const LIST_QUERY_DISJUNCTIONS = 30\n\n/** A very high private-use codepoint: `[p, p + HIGH]` spans every string starting `p`. */\nconst HIGH = '\\uf8ff'\n\nexport interface ListQueryDeclaration {\n /** Every field the Filters panel may name. */\n fields: readonly ListFilterField[]\n /**\n * The orders the list offers, its default FIRST. The query takes no other:\n * each is a composite the index file carries for every filterable field.\n */\n sorts: readonly ListQuerySort[]\n /**\n * The quick search: the token array every write stamps (see\n * `nameSearchTokens`), queried with `array-contains` on one typed word.\n * Absent, the list offers no search box.\n */\n search?: {\n tokensPath: string\n /**\n * Under a scope clause the search cannot be a SECOND array clause, so a\n * scoped list writes `<scopeToken><join><prefix>` tokens and the query\n * asks `array-contains-any` over the scope's tokens joined to the word —\n * one array clause that answers both.\n *\n * ⛔ Rules that grant a list from `visibleTo.hasAny(...)` DENY this\n * query, because it no longer constrains `visibleTo`. Declare it only for\n * readers whose rule needs no `visibleTo` term, or for an Admin-SDK read;\n * see `SCOPED_SEARCH_JOIN` in `@aglyn/aglyn/app-utils/name-search`.\n */\n scoped?: { tokensPath: string; join: string }\n }\n /**\n * Keys a presence-map field (`keysOf`) is filtered by, for the index\n * enumeration: `sources.form`, `sources.order`, …\n */\n keys?: Readonly<Record<string, readonly string[]>>\n}\n\n/** What the list asks this time. */\nexport interface ListQueryRequest {\n clauses: readonly ListFilterRequest[]\n /** The quick search's words. */\n search?: readonly string[]\n /** The order asked for; a sort the declaration does not offer is ignored. */\n sort?: ListQuerySort | null\n /**\n * Predicates the list always applies — its scope — which count against\n * the same limits. An `array-contains-any` scope is what a scoped search\n * folds into (`declaration.search.scoped`).\n */\n base?: readonly ListQueryFilter[]\n}\n\nexport interface ListQueryRefusal {\n /** The clause refused, or `search`. */\n clause: ListFilterRequest | 'search'\n reason: string\n}\n\nexport interface ListQueryPlan {\n /** Every predicate, base included, in the order the SDK should add them. */\n filters: ListQueryFilter[]\n /** The one order: the range field's when there is one, else the sort. */\n orderBy: ListQuerySort\n /** The clauses on the query. */\n served: ListFilterRequest[]\n /** The search word on the query, or null. */\n searched: string | null\n /** What was asked and could not be put on the query, with why. */\n refused: ListQueryRefusal[]\n /** Said to the reader about what WAS served — e.g. one search word of two. */\n notices: string[]\n}\n\n/** How one clause lands on a query, before it is composed with the others. */\ninterface ClauseShape {\n filters: ListQueryFilter[]\n /** The inequality field the clause needs to lead the order, if any. */\n inequality?: string\n /** The clause is the query's array clause. */\n array?: boolean\n /** `in` values, for the disjunction budget. */\n disjunctions?: number\n}\n\nconst csv = (raw: string): string[] =>\n raw\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean)\n\nconst dayBounds = (raw: string): { start: Date; end: Date } | null => {\n const start = listFilterDay(raw)\n if (Number.isNaN(start.getTime())) return null\n const day = new Date(start)\n day.setHours(0, 0, 0, 0)\n const end = new Date(day)\n end.setDate(end.getDate() + 1)\n return { start: day, end }\n}\n\n/** The order a range over `path` puts the list in. */\nconst rangeOrder = (\n declaration: ListQueryDeclaration,\n field: ListFilterField | undefined,\n path: string,\n): ListQuerySort => {\n const offered = declaration.sorts.find((sort) => sort.path === path)\n if (offered) return offered\n return { path, direction: field?.kind === 'date' ? 'desc' : 'asc' }\n}\n\n/**\n * One clause as predicates, or the reason it has none. The same operator\n * meanings as the AGL-2501 twins (`applyListFilter`, `listFilterConstraints`),\n * which this replaces for a list that composes clauses.\n */\nfunction shapeClause(\n field: ListFilterField,\n input: ListFilterRequest,\n names: ListQueryNormalizers,\n): ClauseShape | string {\n if (field.windowOnly) return 'this field is not stored where a query can reach it'\n if (!listFilterOperators(field).includes(input.op)) {\n return `${input.op} is not something this list can ask of ${field.column}`\n }\n const raw = (input.value ?? '').trim()\n const op = input.op\n\n if (op === 'isEmpty') {\n if (field.presence !== 'nullable') return 'only a field stored as null can be asked for empty'\n return { filters: [{ path: field.path, op: '==', value: null }] }\n }\n if (op === 'isNotEmpty') {\n if (field.presence === 'always') return 'this field is never empty'\n return { filters: [{ path: field.path, op: '!=', value: null }], inequality: field.path }\n }\n if (field.kind === 'boolean') {\n if (raw !== 'true' && raw !== 'false') return 'pick true or false'\n return { filters: [{ path: field.path, op: '==', value: raw === 'true' }] }\n }\n if (!raw) return 'no value yet'\n\n if (field.keysOf) {\n if (op !== 'equals' || !/^[A-Za-z0-9_]+$/.test(raw)) return 'pick one of the choices'\n return { filters: [{ path: `${field.path}.${raw}`, op: '==', value: true }] }\n }\n\n if (field.kind === 'text') {\n if (op === 'contains' && field.tokensPath) {\n const token = field.verbatimTokens ? raw : names.token(raw)\n if (!token) return 'no value yet'\n return {\n filters: [{ path: field.tokensPath, op: 'array-contains', value: token }],\n array: true,\n }\n }\n if (op === 'equals' && field.lowerPath) {\n return { filters: [{ path: field.lowerPath, op: '==', value: names.key(raw) }] }\n }\n if (op === 'equals' && field.presence === 'always' && !field.lowerPath) {\n return { filters: [{ path: field.path, op: '==', value: raw }] }\n }\n if (op === 'startsWith' && field.lowerPath) {\n const key = names.key(raw)\n return {\n filters: [\n { path: field.lowerPath, op: '>=', value: key },\n { path: field.lowerPath, op: '<=', value: `${key}${HIGH}` },\n ],\n inequality: field.lowerPath,\n }\n }\n if (op === 'endsWith' && field.reversedPath) {\n const key = names.reversed(raw)\n return {\n filters: [\n { path: field.reversedPath, op: '>=', value: key },\n { path: field.reversedPath, op: '<=', value: `${key}${HIGH}` },\n ],\n inequality: field.reversedPath,\n }\n }\n if (op === 'isAnyOf' && field.lowerPath) {\n const values = csv(raw).map((value) => names.key(value))\n if (values.length > LIST_QUERY_DISJUNCTIONS) return `at most ${LIST_QUERY_DISJUNCTIONS} values`\n return {\n filters: [{ path: field.lowerPath, op: 'in', value: values }],\n disjunctions: values.length,\n }\n }\n return `${op} is not something this list can ask of ${field.column}`\n }\n\n if (field.kind === 'id') {\n if (op === 'equals') return { filters: [{ path: LIST_QUERY_ID_PATH, op: '==', value: raw }] }\n if (op === 'isAnyOf') {\n const values = csv(raw)\n if (values.length > LIST_QUERY_DISJUNCTIONS) return `at most ${LIST_QUERY_DISJUNCTIONS} values`\n return {\n filters: [{ path: LIST_QUERY_ID_PATH, op: 'in', value: values }],\n disjunctions: values.length,\n }\n }\n if (op === 'startsWith') {\n return {\n filters: [\n { path: LIST_QUERY_ID_PATH, op: '>=', value: raw },\n { path: LIST_QUERY_ID_PATH, op: '<=', value: `${raw}${HIGH}` },\n ],\n inequality: LIST_QUERY_ID_PATH,\n }\n }\n return `${op} is not something this list can ask of ${field.column}`\n }\n\n if (field.kind === 'exact') {\n if (op === 'equals') return { filters: [{ path: field.path, op: '==', value: raw }] }\n if (op === 'isAnyOf') {\n const values = csv(raw)\n if (values.length > LIST_QUERY_DISJUNCTIONS) return `at most ${LIST_QUERY_DISJUNCTIONS} values`\n if (field.tokensPath) {\n // An array field asked \"any of these\": one array clause.\n return {\n filters: [{ path: field.tokensPath, op: 'array-contains-any', value: values }],\n array: true,\n disjunctions: values.length,\n }\n }\n return {\n filters: [{ path: field.path, op: 'in', value: values }],\n disjunctions: values.length,\n }\n }\n if (op === 'contains' && field.tokensPath) {\n return {\n filters: [{ path: field.tokensPath, op: 'array-contains', value: raw }],\n array: true,\n }\n }\n return `${op} is not something this list can ask of ${field.column}`\n }\n\n if (field.kind === 'number') {\n const value = Number(raw)\n if (!Number.isFinite(value)) return 'type a number'\n if (op === '=') return { filters: [{ path: field.path, op: '==', value }] }\n const comparison: Record<string, ListQueryOp> = {\n '!=': '!=',\n '>': '>',\n '>=': '>=',\n '<': '<',\n '<=': '<=',\n }\n const found = comparison[op]\n if (!found) return `${op} is not something this list can ask of ${field.column}`\n return { filters: [{ path: field.path, op: found, value }], inequality: field.path }\n }\n\n if (field.kind === 'date') {\n const day = dayBounds(raw)\n if (!day) return 'pick a date'\n // A `…AtMs` field is compared as the number it is stored as.\n const at = (date: Date): Date | number =>\n field.storedAs === 'millis' ? date.getTime() : date\n if (op === 'is') {\n return {\n filters: [\n { path: field.path, op: '>=', value: at(day.start) },\n { path: field.path, op: '<', value: at(day.end) },\n ],\n inequality: field.path,\n }\n }\n const bound: Record<string, [ListQueryOp, Date]> = {\n after: ['>=', day.end],\n onOrAfter: ['>=', day.start],\n before: ['<', day.start],\n onOrBefore: ['<', day.end],\n }\n const found = bound[op]\n if (!found) return `${op} is not something this list can ask of ${field.column}`\n return { filters: [{ path: field.path, op: found[0], value: at(found[1]) }], inequality: field.path }\n }\n\n return `${op} is not something this list can ask of ${field.column}`\n}\n\nconst disjunctionsOf = (filter: ListQueryFilter): number =>\n filter.op === 'in' || filter.op === 'array-contains-any'\n ? Math.max(1, Array.isArray(filter.value) ? filter.value.length : 1)\n : 1\n\n/**\n * Every clause and the search, as ONE query — or as much of it as one query\n * can hold, with the rest refused by name.\n *\n * Order of claim: the base (the list's scope) first, then the search, then\n * the clauses in the order they were added. A clause that would be a second\n * array clause, a second inequality field or push the disjunctions past\n * thirty is refused, and the list shows the refusal beside its chip; the\n * clauses before it stand. The search is claimed before the clauses because\n * it is the thing a reader typed last and looks at.\n */\nexport function planListQuery(\n declaration: ListQueryDeclaration,\n request: ListQueryRequest,\n names: ListQueryNormalizers,\n): ListQueryPlan {\n const filters: ListQueryFilter[] = [...(request.base ?? [])]\n const served: ListFilterRequest[] = []\n const refused: ListQueryRefusal[] = []\n const notices: string[] = []\n let arrayTaken = filters.some(\n (filter) => filter.op === 'array-contains' || filter.op === 'array-contains-any',\n )\n let inequality: string | null =\n filters.find((filter) => ['!=', '<', '<=', '>', '>='].includes(filter.op))?.path ?? null\n let disjunctions = filters.reduce((product, filter) => product * disjunctionsOf(filter), 1)\n\n // The search.\n let searched: string | null = null\n const words = (request.search ?? []).map((word) => word.trim()).filter(Boolean)\n if (words.length) {\n const search = declaration.search\n const token = names.token(words.join(' '))\n if (!search) {\n refused.push({ clause: 'search', reason: 'this list has no search' })\n } else if (!token) {\n // Nothing searchable was typed.\n } else {\n const scopeAt = filters.findIndex(\n (filter) => filter.op === 'array-contains-any' || filter.op === 'array-contains',\n )\n if (scopeAt === -1) {\n filters.push({ path: search.tokensPath, op: 'array-contains', value: token })\n arrayTaken = true\n searched = token\n } else if (search.scoped) {\n // Fold the search into the scope: one array clause answers both.\n const scope = filters[scopeAt]\n const scopes = Array.isArray(scope.value) ? scope.value : [scope.value]\n filters.splice(scopeAt, 1, {\n path: search.scoped.tokensPath,\n op: 'array-contains-any',\n value: scopes.map((entry) => `${String(entry)}${search.scoped?.join}${token}`),\n })\n searched = token\n } else {\n refused.push({\n clause: 'search',\n reason: 'this list is already narrowed to what you can see, which search cannot combine with',\n })\n }\n if (searched && words.length > 1) {\n notices.push(\n `Search matches one word at a time: showing results for \"${token}\".`,\n )\n }\n if (searched && names.key(words[0]).length > names.maxPrefix) {\n notices.push(`Search reads the first ${names.maxPrefix} letters of a word.`)\n }\n }\n }\n\n // The clauses.\n for (const clause of request.clauses) {\n const field = declaration.fields.find((entry) => entry.column === clause.field)\n if (!field) {\n refused.push({ clause, reason: 'this list does not filter by that' })\n continue\n }\n const shape = shapeClause(field, clause, names)\n if (typeof shape === 'string') {\n refused.push({ clause, reason: shape })\n continue\n }\n if (shape.array && arrayTaken) {\n refused.push({\n clause,\n reason: searched\n ? 'cannot be combined with the search — clear the search to use it'\n : 'cannot be combined with another \"contains\" or \"any of\" filter on a list',\n })\n continue\n }\n if (shape.inequality && inequality && shape.inequality !== inequality) {\n refused.push({\n clause,\n reason: 'only one range (dates, numbers, starts with) can apply at a time',\n })\n continue\n }\n const product =\n disjunctions * shape.filters.reduce((total, filter) => total * disjunctionsOf(filter), 1)\n if (product > LIST_QUERY_DISJUNCTIONS) {\n refused.push({ clause, reason: `too many values at once (the limit is ${LIST_QUERY_DISJUNCTIONS})` })\n continue\n }\n filters.push(...shape.filters)\n served.push(clause)\n if (shape.array) arrayTaken = true\n if (shape.inequality) inequality = shape.inequality\n disjunctions = product\n }\n\n // The order.\n const asked = request.sort\n ? declaration.sorts.find(\n (sort) => sort.path === request.sort?.path && sort.direction === request.sort?.direction,\n )\n : undefined\n // A range leads the order with the field it ranges over — in the asked\n // direction when the asked order IS that field, since a declared order has\n // its composites either way.\n const orderBy = inequality\n ? asked?.path === inequality\n ? asked\n : rangeOrder(\n declaration,\n declaration.fields.find(\n (field) =>\n field.path === inequality ||\n field.lowerPath === inequality ||\n field.reversedPath === inequality,\n ),\n inequality,\n )\n : (asked ?? declaration.sorts[0] ?? { path: LIST_QUERY_ID_PATH, direction: 'asc' })\n\n return { filters, orderBy, served, searched, refused, notices }\n}\n\n/** A composite the plan's shapes need, in the index file's own terms. */\nexport interface ListQueryIndex {\n fields: Array<{ fieldPath: string; order?: 'ASCENDING' | 'DESCENDING'; arrayConfig?: 'CONTAINS' }>\n}\n\nconst ORDER = { asc: 'ASCENDING', desc: 'DESCENDING' } as const\n\n/**\n * Every composite a declaration's queries can need, under index merging:\n * one `(predicate field, order)` index per equality or array field and per\n * order the list can take — its sorts, and the order each range imposes.\n *\n * `base` names the scope predicates the list always applies (their paths\n * and whether they are array clauses), which need their own index per order\n * like any other equality. Pinned by each list's spec against\n * `cloud/firebase-firestore.indexes.json`.\n */\nexport function listQueryIndexes(\n declaration: ListQueryDeclaration,\n base: readonly { path: string; array?: boolean }[] = [],\n): ListQueryIndex[] {\n const equalities = new Map<string, boolean>() // path → is array clause\n const orders: ListQuerySort[] = [...declaration.sorts]\n const addOrder = (sort: ListQuerySort) => {\n if (!orders.some((entry) => entry.path === sort.path && entry.direction === sort.direction)) {\n orders.push(sort)\n }\n }\n for (const entry of base) equalities.set(entry.path, Boolean(entry.array))\n if (declaration.search) {\n equalities.set(declaration.search.tokensPath, true)\n if (declaration.search.scoped) equalities.set(declaration.search.scoped.tokensPath, true)\n }\n for (const field of declaration.fields) {\n if (field.windowOnly) continue\n const ops = listFilterOperators(field)\n const has = (...names: string[]) => names.some((name) => ops.includes(name))\n if (field.keysOf) {\n for (const key of declaration.keys?.[field.column] ?? []) {\n equalities.set(`${field.path}.${key}`, false)\n }\n continue\n }\n if (field.kind === 'text') {\n if (has('contains') && field.tokensPath) equalities.set(field.tokensPath, true)\n if (has('equals', 'isAnyOf') && field.lowerPath) equalities.set(field.lowerPath, false)\n if (has('equals') && !field.lowerPath && field.presence === 'always') equalities.set(field.path, false)\n if (has('startsWith') && field.lowerPath) addOrder(rangeOrder(declaration, field, field.lowerPath))\n if (has('endsWith') && field.reversedPath) addOrder(rangeOrder(declaration, field, field.reversedPath))\n } else if (field.kind === 'exact') {\n if (has('equals', 'isAnyOf')) equalities.set(field.path, false)\n if (has('contains', 'isAnyOf') && field.tokensPath) equalities.set(field.tokensPath, true)\n } else if (field.kind === 'boolean') {\n equalities.set(field.path, false)\n } else if (field.kind === 'number') {\n if (has('=')) equalities.set(field.path, false)\n if (has('!=', '>', '>=', '<', '<=')) addOrder(rangeOrder(declaration, field, field.path))\n } else if (field.kind === 'date') {\n if (has('is', 'after', 'onOrAfter', 'before', 'onOrBefore')) {\n addOrder(rangeOrder(declaration, field, field.path))\n }\n }\n if (has('isEmpty')) equalities.set(field.path, false)\n if (has('isNotEmpty')) addOrder(rangeOrder(declaration, field, field.path))\n }\n const indexes: ListQueryIndex[] = []\n const seen = new Set<string>()\n for (const [path, array] of equalities) {\n for (const order of orders) {\n if (order.path === path || order.path === LIST_QUERY_ID_PATH) continue\n const index: ListQueryIndex = {\n fields: [\n array ? { fieldPath: path, arrayConfig: 'CONTAINS' } : { fieldPath: path, order: 'ASCENDING' },\n { fieldPath: order.path, order: ORDER[order.direction] },\n ],\n }\n const key = JSON.stringify(index)\n if (!seen.has(key)) {\n seen.add(key)\n indexes.push(index)\n }\n }\n }\n return indexes\n}\n\n/** An index entry as `cloud/firebase-firestore.indexes.json` stores it. */\ninterface IndexFileEntry {\n collectionGroup: string\n queryScope: string\n fields: Array<{ fieldPath: string; order?: string; arrayConfig?: string }>\n}\n\n/**\n * The composites a declaration needs that an index file does not hold — what\n * a list's spec asserts is empty, so a declared filter cannot ship without\n * the index that serves it.\n */\nexport function missingListQueryIndexes(\n file: { indexes: readonly IndexFileEntry[] },\n collectionGroup: string,\n needed: readonly ListQueryIndex[],\n queryScope: 'COLLECTION' | 'COLLECTION_GROUP' = 'COLLECTION',\n): ListQueryIndex[] {\n const shape = (fields: IndexFileEntry['fields']) =>\n fields\n .filter((field) => field.fieldPath !== LIST_QUERY_ID_PATH)\n .map((field) => `${field.fieldPath}:${field.order ?? field.arrayConfig}`)\n .join(',')\n const present = new Set(\n file.indexes\n .filter((index) => index.collectionGroup === collectionGroup && index.queryScope === queryScope)\n .map((index) => shape(index.fields)),\n )\n return needed.filter((index) => !present.has(shape(index.fields)))\n}\n"],"names":["listFilterDay","listFilterOperators","LIST_QUERY_ID_PATH","LIST_QUERY_DISJUNCTIONS","HIGH","csv","raw","split","map","entry","trim","filter","Boolean","dayBounds","start","Number","isNaN","getTime","day","Date","setHours","end","setDate","getDate","rangeOrder","declaration","field","path","offered","sorts","find","sort","direction","kind","shapeClause","input","names","windowOnly","includes","op","column","value","presence","filters","inequality","keysOf","test","tokensPath","token","verbatimTokens","array","lowerPath","key","reversedPath","reversed","values","length","disjunctions","isFinite","comparison","found","at","date","storedAs","bound","after","onOrAfter","before","onOrBefore","disjunctionsOf","Math","max","Array","isArray","planListQuery","request","asked","base","served","refused","notices","arrayTaken","some","reduce","product","searched","words","search","word","join","push","clause","reason","scopeAt","findIndex","scoped","scope","scopes","splice","String","maxPrefix","clauses","fields","shape","total","undefined","orderBy","ORDER","asc","desc","listQueryIndexes","equalities","Map","orders","addOrder","set","ops","has","name","keys","indexes","seen","Set","order","index","fieldPath","arrayConfig","JSON","stringify","add","missingListQueryIndexes","file","collectionGroup","needed","queryScope","present"],"mappings":"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"}
|