@aglyn/plugins-data 1.0.0-beta.234 → 1.0.0-beta.235

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.
@@ -15,77 +15,23 @@
15
15
  * limitations under the License.
16
16
  */ import { _ as _extends } from "@swc/helpers/_/_extends";
17
17
  import { validateCustomFieldValue } from "@aglyn/aglyn/plugin-manager/custom-fields";
18
- /** Display fallback for raw ids: "roast_preference" → "Roast preference". */ export function humanizeDatasetFieldId(id) {
19
- const words = id.replace(/_/g, ' ').trim();
20
- return words ? words.charAt(0).toUpperCase() + words.slice(1) : id;
21
- }
22
- export const DATASET_FIELD_TYPES = [
23
- 'text',
24
- 'bool',
25
- 'int32',
26
- 'int64',
27
- 'float',
28
- 'timestamp',
29
- 'coordinates',
30
- 'map',
31
- 'sorted',
32
- 'bytes',
33
- 'nil',
34
- 'reference'
35
- ];
36
- /** Display names, mirroring dod.ts `lbl`. */ export const DATASET_FIELD_TYPE_LABELS = {
37
- text: 'Text',
38
- bool: 'Boolean',
39
- int32: 'Integer',
40
- int64: 'Big integer',
41
- float: 'Number',
42
- timestamp: 'Date & time',
43
- coordinates: 'Coordinates',
44
- map: 'Map',
45
- sorted: 'List',
46
- bytes: 'Bytes',
47
- nil: 'Null',
48
- reference: 'Reference'
49
- };
50
- /**
51
- * Migration shim (v1 → v2): datasets created before models carry flat
52
- * `fields: string[]` — derive a model where every column is an optional
53
- * text field whose id is the column name, so the editor and AGL-103
54
- * bindings keep working on unmigrated docs during rollout.
55
- */ export function deriveModelFromFields(fields) {
56
- const model = {
57
- fields: {},
58
- order: []
59
- };
60
- for (const name of fields){
61
- // Raw snake_case ids read as titles in table headers and pickers;
62
- // the id stays the stable key (AGL-558).
63
- model.fields[name] = {
64
- name: humanizeDatasetFieldId(name),
65
- type: 'text'
66
- };
67
- model.order.push(name);
68
- }
69
- return model;
70
- }
18
+ import { datasetFilterKeys, datasetFilterValues } from "./dataset-model-core.js";
71
19
  /**
72
- * Model from quick-creator entries (AGL-558): stable slug ids with the
73
- * human display names preserved — "Roast preference" → id
74
- * `roast_preference`, name "Roast preference".
75
- */ export function modelFromFieldEntries(entries) {
76
- const model = {
77
- fields: {},
78
- order: []
79
- };
80
- for (const entry of entries){
81
- model.fields[entry.id] = {
82
- name: entry.name,
83
- type: 'text'
84
- };
85
- model.order.push(entry.id);
86
- }
87
- return model;
88
- }
20
+ * Dataset models (AGL-177): the runtime promotion of the type-level
21
+ * blueprint in `libs/shared/data/types/src/lib/dod.ts` (kept as the
22
+ * referenced design doc — this module is the single runtime source of
23
+ * truth). A model lives on the dataset doc
24
+ * (`orgs/{orgId}/datasets/{id}.model`) and drives the typed editor, import
25
+ * and site-restore validation, and every record write — the console, `/v1`,
26
+ * a bound form's submission and an automation step — through the shared
27
+ * `validateDocument`/`coerceDocumentValues` pair so they can't disagree. The
28
+ * form and the automation step reach the pair through
29
+ * `prepareDatasetRecordWrite` (AGL-2773).
30
+ *
31
+ * Storage conventions (Firestore-safe): timestamps as epoch millis
32
+ * numbers, coordinates as `{ latitude, longitude }`, `sorted` as arrays,
33
+ * `map` as plain objects, references as target-document id strings.
34
+ */ export * from "./dataset-model-core.js";
89
35
  /**
90
36
  * The part of a dataset's model a repeat reads (AGL-180): each reference
91
37
  * field, by the id of the dataset it points into, which is a key the rows map
@@ -102,14 +48,6 @@ export const DATASET_FIELD_TYPES = [
102
48
  references
103
49
  };
104
50
  }
105
- /** The dataset's model, deriving one from v1 `fields` when absent. */ export function effectiveDatasetModel(dataset) {
106
- var _dataset_fields;
107
- var _dataset_model, _dataset_model_order;
108
- if (((_dataset_model = dataset.model) == null ? void 0 : _dataset_model.fields) && ((_dataset_model_order = dataset.model.order) == null ? void 0 : _dataset_model_order.length)) {
109
- return dataset.model;
110
- }
111
- return deriveModelFromFields((_dataset_fields = dataset.fields) != null ? _dataset_fields : []);
112
- }
113
51
  const isFiniteNumber = (value)=>typeof value === 'number' && Number.isFinite(value);
114
52
  /**
115
53
  * Validates a document's values against a model, returning fieldId →
@@ -233,48 +171,6 @@ const isFiniteNumber = (value)=>typeof value === 'number' && Number.isFinite(val
233
171
  }
234
172
  return errors;
235
173
  }
236
- /** Storage value → grid display string (AGL-179). */ export function formatDatasetValue(field, value) {
237
- if (value == null || value === '') return '';
238
- switch(field.type){
239
- case 'bool':
240
- return value === true ? '✓' : value === false ? '—' : String(value);
241
- case 'timestamp':
242
- return isFiniteNumber(value) ? new Date(value).toISOString().slice(0, 16).replace('T', ' ') : String(value);
243
- case 'coordinates':
244
- {
245
- const coordinates = value;
246
- return isFiniteNumber(coordinates == null ? void 0 : coordinates.latitude) && isFiniteNumber(coordinates == null ? void 0 : coordinates.longitude) ? `${coordinates.latitude}, ${coordinates.longitude}` : String(value);
247
- }
248
- case 'sorted':
249
- return Array.isArray(value) ? value.join(', ') : String(value);
250
- case 'map':
251
- try {
252
- return JSON.stringify(value);
253
- } catch (unused) {
254
- return String(value);
255
- }
256
- default:
257
- return String(value);
258
- }
259
- }
260
- /**
261
- * Storage value → form-input string (AGL-179): the inverse of
262
- * `coerceDocumentValues` for populating type-appropriate inputs
263
- * (timestamps as `datetime-local` values, coordinates as "lat, lon").
264
- */ export function datasetValueToInput(field, value) {
265
- if (value == null) return '';
266
- if (field.type === 'timestamp' && isFiniteNumber(value)) {
267
- return new Date(value).toISOString().slice(0, 16);
268
- }
269
- if (field.type === 'bool') {
270
- return value === true ? 'true' : value === false ? 'false' : String(value);
271
- }
272
- if (field.type === 'coordinates' || field.type === 'sorted') {
273
- return formatDatasetValue(field, value);
274
- }
275
- if (field.type === 'map') return formatDatasetValue(field, value);
276
- return String(value);
277
- }
278
174
  /**
279
175
  * Whether a field stores a list: `sorted`, and a `reference` that may point at
280
176
  * more than one record. Both go through {@link coerceListValue}.
@@ -530,277 +426,5 @@ const BOOL_FALSE_WORDS = new Set([
530
426
  filterValues: Object.keys(filterValues).length ? filterValues : clear
531
427
  };
532
428
  }
533
- /**
534
- * A dataset record's FILTER TOKENS: every word-level question the records
535
- * table can ask of a record, flattened into one array of strings Firestore
536
- * indexes on its own. Its twin, {@link datasetFilterValues}, holds the
537
- * equalities.
538
- *
539
- * ## Why these fields, and not the values
540
- *
541
- * A record's fields live under `values`, a user-defined, unbounded map that
542
- * the index configuration exempts from indexing — auto-indexing it can exceed
543
- * Firestore's per-document index-entry limit — so `where('values.<field>', …)`
544
- * is refused. A composite index per dataset would need an admin call per
545
- * dataset, runs into the project's composite-index cap, and is deleted by the
546
- * next `firestore:indexes` deploy of a file that does not list it.
547
- *
548
- * `filterKeys` and `filterValues` need none of that. Firestore's AUTOMATIC
549
- * single-field indexes serve `where('filterKeys', 'array-contains', token)`
550
- * and `where('filterValues.<field>', '==', value)`, and every automatic index
551
- * ends in the document name — so any number of those equalities and ONE
552
- * `array-contains`, under `orderBy(documentId())`, compose by index merging,
553
- * with no composite index and no index deploy, on any dataset of any project
554
- * (AGL-3321).
555
- *
556
- * ## The token grammar
557
- *
558
- * `f:<fieldId>=<value>` a field equals a value. Enum (a text field with
559
- * `validation.options`) values are kept exactly;
560
- * plain text is lower-cased, trimmed and clipped to
561
- * {@link DATASET_FILTER_VALUE_MAX} characters;
562
- * booleans are `true`/`false`; numbers are
563
- * `String(number)`; a `sorted` list contributes one
564
- * token per member.
565
- * `f:<fieldId>^<prefix>` a word of a plain text field starts with this.
566
- * `s:<prefix>` a word of ANY text value (enums and list members
567
- * included) starts with this — the quick search.
568
- *
569
- * Timestamps, references, maps, bytes, coordinates and nil fields carry no
570
- * field tokens: a timestamp is asked in ranges, which one token cannot serve,
571
- * and the rest are not asked about by value.
572
- *
573
- * The array is sorted, de-duplicated and capped at
574
- * {@link DATASET_FILTER_KEYS_MAX}. Past the cap, the `=` tokens are kept
575
- * first, then the per-field word prefixes, then the search prefixes, so a
576
- * very long record loses search reach before it loses exact matches.
577
- */ export const DATASET_FILTER_KEYS_MAX = 500;
578
- /** Plain text values are clipped to this many characters in an `=` token. */ export const DATASET_FILTER_VALUE_MAX = 64;
579
- /** Word prefixes run from 1 to this many characters. */ export const DATASET_FILTER_PREFIX_MAX = 12;
580
- /** Only the first this-many words of a value contribute prefixes. */ export const DATASET_FILTER_WORDS_MAX = 40;
581
- /**
582
- * The words of a text value: split on anything that is not a letter or a
583
- * digit, lower-cased, at most {@link DATASET_FILTER_WORDS_MAX} of them.
584
- */ export function datasetFilterWords(text) {
585
- return String(text).toLowerCase().split(RegExp("[^\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376-\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0560-\\u0588\\u05D0-\\u05EA\\u05EF-\\u05F2\\u0620-\\u064A\\u066E-\\u066F\\u0671-\\u06D3\\u06D5\\u06E5-\\u06E6\\u06EE-\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4-\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u0860-\\u086A\\u0870-\\u0887\\u0889-\\u088F\\u08A0-\\u08C9\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F-\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC-\\u09DD\\u09DF-\\u09E1\\u09F0-\\u09F1\\u09FC\\u0A05-\\u0A0A\\u0A0F-\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32-\\u0A33\\u0A35-\\u0A36\\u0A38-\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2-\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0-\\u0AE1\\u0AF9\\u0B05-\\u0B0C\\u0B0F-\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32-\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C-\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99-\\u0B9A\\u0B9C\\u0B9E-\\u0B9F\\u0BA3-\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58-\\u0C5A\\u0C5C-\\u0C5D\\u0C60-\\u0C61\\u0C80\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDC-\\u0CDE\\u0CE0-\\u0CE1\\u0CF1-\\u0CF2\\u0D04-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D54-\\u0D56\\u0D5F-\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32-\\u0E33\\u0E40-\\u0E46\\u0E81-\\u0E82\\u0E84\\u0E86-\\u0E8A\\u0E8C-\\u0EA3\\u0EA5\\u0EA7-\\u0EB0\\u0EB2-\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065-\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F5\\u13F8-\\u13FD\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16F1-\\u16F8\\u1700-\\u1711\\u171F-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1878\\u1880-\\u1884\\u1887-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4C\\u1B83-\\u1BA0\\u1BAE-\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1C80-\\u1C8A\\u1C90-\\u1CBA\\u1CBD-\\u1CBF\\u1CE9-\\u1CEC\\u1CEE-\\u1CF3\\u1CF5-\\u1CF6\\u1CFA\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183-\\u2184\\u2C00-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2-\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3006\\u3031-\\u3035\\u303B-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312F\\u3131-\\u318E\\u31A0-\\u31BF\\u31F0-\\u31FF\\u3400-\\u4DBF\\u4E00-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A-\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6E5\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA7DC\\uA7F1-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA8FD-\\uA8FE\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5-\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB69\\uAB70-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40-\\uFB41\\uFB43-\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC\\u{10000}-\\u{1000B}\\u{1000D}-\\u{10026}\\u{10028}-\\u{1003A}\\u{1003C}-\\u{1003D}\\u{1003F}-\\u{1004D}\\u{10050}-\\u{1005D}\\u{10080}-\\u{100FA}\\u{10280}-\\u{1029C}\\u{102A0}-\\u{102D0}\\u{10300}-\\u{1031F}\\u{1032D}-\\u{10340}\\u{10342}-\\u{10349}\\u{10350}-\\u{10375}\\u{10380}-\\u{1039D}\\u{103A0}-\\u{103C3}\\u{103C8}-\\u{103CF}\\u{10400}-\\u{1049D}\\u{104B0}-\\u{104D3}\\u{104D8}-\\u{104FB}\\u{10500}-\\u{10527}\\u{10530}-\\u{10563}\\u{10570}-\\u{1057A}\\u{1057C}-\\u{1058A}\\u{1058C}-\\u{10592}\\u{10594}-\\u{10595}\\u{10597}-\\u{105A1}\\u{105A3}-\\u{105B1}\\u{105B3}-\\u{105B9}\\u{105BB}-\\u{105BC}\\u{105C0}-\\u{105F3}\\u{10600}-\\u{10736}\\u{10740}-\\u{10755}\\u{10760}-\\u{10767}\\u{10780}-\\u{10785}\\u{10787}-\\u{107B0}\\u{107B2}-\\u{107BA}\\u{10800}-\\u{10805}\\u{10808}\\u{1080A}-\\u{10835}\\u{10837}-\\u{10838}\\u{1083C}\\u{1083F}-\\u{10855}\\u{10860}-\\u{10876}\\u{10880}-\\u{1089E}\\u{108E0}-\\u{108F2}\\u{108F4}-\\u{108F5}\\u{10900}-\\u{10915}\\u{10920}-\\u{10939}\\u{10940}-\\u{10959}\\u{10980}-\\u{109B7}\\u{109BE}-\\u{109BF}\\u{10A00}\\u{10A10}-\\u{10A13}\\u{10A15}-\\u{10A17}\\u{10A19}-\\u{10A35}\\u{10A60}-\\u{10A7C}\\u{10A80}-\\u{10A9C}\\u{10AC0}-\\u{10AC7}\\u{10AC9}-\\u{10AE4}\\u{10B00}-\\u{10B35}\\u{10B40}-\\u{10B55}\\u{10B60}-\\u{10B72}\\u{10B80}-\\u{10B91}\\u{10C00}-\\u{10C48}\\u{10C80}-\\u{10CB2}\\u{10CC0}-\\u{10CF2}\\u{10D00}-\\u{10D23}\\u{10D4A}-\\u{10D65}\\u{10D6F}-\\u{10D85}\\u{10E80}-\\u{10EA9}\\u{10EB0}-\\u{10EB1}\\u{10EC2}-\\u{10EC7}\\u{10F00}-\\u{10F1C}\\u{10F27}\\u{10F30}-\\u{10F45}\\u{10F70}-\\u{10F81}\\u{10FB0}-\\u{10FC4}\\u{10FE0}-\\u{10FF6}\\u{11003}-\\u{11037}\\u{11071}-\\u{11072}\\u{11075}\\u{11083}-\\u{110AF}\\u{110D0}-\\u{110E8}\\u{11103}-\\u{11126}\\u{11144}\\u{11147}\\u{11150}-\\u{11172}\\u{11176}\\u{11183}-\\u{111B2}\\u{111C1}-\\u{111C4}\\u{111DA}\\u{111DC}\\u{11200}-\\u{11211}\\u{11213}-\\u{1122B}\\u{1123F}-\\u{11240}\\u{11280}-\\u{11286}\\u{11288}\\u{1128A}-\\u{1128D}\\u{1128F}-\\u{1129D}\\u{1129F}-\\u{112A8}\\u{112B0}-\\u{112DE}\\u{11305}-\\u{1130C}\\u{1130F}-\\u{11310}\\u{11313}-\\u{11328}\\u{1132A}-\\u{11330}\\u{11332}-\\u{11333}\\u{11335}-\\u{11339}\\u{1133D}\\u{11350}\\u{1135D}-\\u{11361}\\u{11380}-\\u{11389}\\u{1138B}\\u{1138E}\\u{11390}-\\u{113B5}\\u{113B7}\\u{113D1}\\u{113D3}\\u{11400}-\\u{11434}\\u{11447}-\\u{1144A}\\u{1145F}-\\u{11461}\\u{11480}-\\u{114AF}\\u{114C4}-\\u{114C5}\\u{114C7}\\u{11580}-\\u{115AE}\\u{115D8}-\\u{115DB}\\u{11600}-\\u{1162F}\\u{11644}\\u{11680}-\\u{116AA}\\u{116B8}\\u{11700}-\\u{1171A}\\u{11740}-\\u{11746}\\u{11800}-\\u{1182B}\\u{118A0}-\\u{118DF}\\u{118FF}-\\u{11906}\\u{11909}\\u{1190C}-\\u{11913}\\u{11915}-\\u{11916}\\u{11918}-\\u{1192F}\\u{1193F}\\u{11941}\\u{119A0}-\\u{119A7}\\u{119AA}-\\u{119D0}\\u{119E1}\\u{119E3}\\u{11A00}\\u{11A0B}-\\u{11A32}\\u{11A3A}\\u{11A50}\\u{11A5C}-\\u{11A89}\\u{11A9D}\\u{11AB0}-\\u{11AF8}\\u{11BC0}-\\u{11BE0}\\u{11C00}-\\u{11C08}\\u{11C0A}-\\u{11C2E}\\u{11C40}\\u{11C72}-\\u{11C8F}\\u{11D00}-\\u{11D06}\\u{11D08}-\\u{11D09}\\u{11D0B}-\\u{11D30}\\u{11D46}\\u{11D60}-\\u{11D65}\\u{11D67}-\\u{11D68}\\u{11D6A}-\\u{11D89}\\u{11D98}\\u{11DB0}-\\u{11DDB}\\u{11EE0}-\\u{11EF2}\\u{11F02}\\u{11F04}-\\u{11F10}\\u{11F12}-\\u{11F33}\\u{11FB0}\\u{12000}-\\u{12399}\\u{12480}-\\u{12543}\\u{12F90}-\\u{12FF0}\\u{13000}-\\u{1342F}\\u{13441}-\\u{13446}\\u{13460}-\\u{143FA}\\u{14400}-\\u{14646}\\u{16100}-\\u{1611D}\\u{16800}-\\u{16A38}\\u{16A40}-\\u{16A5E}\\u{16A70}-\\u{16ABE}\\u{16AD0}-\\u{16AED}\\u{16B00}-\\u{16B2F}\\u{16B40}-\\u{16B43}\\u{16B63}-\\u{16B77}\\u{16B7D}-\\u{16B8F}\\u{16D40}-\\u{16D6C}\\u{16E40}-\\u{16E7F}\\u{16EA0}-\\u{16EB8}\\u{16EBB}-\\u{16ED3}\\u{16F00}-\\u{16F4A}\\u{16F50}\\u{16F93}-\\u{16F9F}\\u{16FE0}-\\u{16FE1}\\u{16FE3}\\u{16FF2}-\\u{16FF3}\\u{17000}-\\u{18CD5}\\u{18CFF}-\\u{18D1E}\\u{18D80}-\\u{18DF2}\\u{1AFF0}-\\u{1AFF3}\\u{1AFF5}-\\u{1AFFB}\\u{1AFFD}-\\u{1AFFE}\\u{1B000}-\\u{1B122}\\u{1B132}\\u{1B150}-\\u{1B152}\\u{1B155}\\u{1B164}-\\u{1B167}\\u{1B170}-\\u{1B2FB}\\u{1BC00}-\\u{1BC6A}\\u{1BC70}-\\u{1BC7C}\\u{1BC80}-\\u{1BC88}\\u{1BC90}-\\u{1BC99}\\u{1D400}-\\u{1D454}\\u{1D456}-\\u{1D49C}\\u{1D49E}-\\u{1D49F}\\u{1D4A2}\\u{1D4A5}-\\u{1D4A6}\\u{1D4A9}-\\u{1D4AC}\\u{1D4AE}-\\u{1D4B9}\\u{1D4BB}\\u{1D4BD}-\\u{1D4C3}\\u{1D4C5}-\\u{1D505}\\u{1D507}-\\u{1D50A}\\u{1D50D}-\\u{1D514}\\u{1D516}-\\u{1D51C}\\u{1D51E}-\\u{1D539}\\u{1D53B}-\\u{1D53E}\\u{1D540}-\\u{1D544}\\u{1D546}\\u{1D54A}-\\u{1D550}\\u{1D552}-\\u{1D6A5}\\u{1D6A8}-\\u{1D6C0}\\u{1D6C2}-\\u{1D6DA}\\u{1D6DC}-\\u{1D6FA}\\u{1D6FC}-\\u{1D714}\\u{1D716}-\\u{1D734}\\u{1D736}-\\u{1D74E}\\u{1D750}-\\u{1D76E}\\u{1D770}-\\u{1D788}\\u{1D78A}-\\u{1D7A8}\\u{1D7AA}-\\u{1D7C2}\\u{1D7C4}-\\u{1D7CB}\\u{1DF00}-\\u{1DF1E}\\u{1DF25}-\\u{1DF2A}\\u{1E030}-\\u{1E06D}\\u{1E100}-\\u{1E12C}\\u{1E137}-\\u{1E13D}\\u{1E14E}\\u{1E290}-\\u{1E2AD}\\u{1E2C0}-\\u{1E2EB}\\u{1E4D0}-\\u{1E4EB}\\u{1E5D0}-\\u{1E5ED}\\u{1E5F0}\\u{1E6C0}-\\u{1E6DE}\\u{1E6E0}-\\u{1E6E2}\\u{1E6E4}-\\u{1E6E5}\\u{1E6E7}-\\u{1E6ED}\\u{1E6F0}-\\u{1E6F4}\\u{1E6FE}-\\u{1E6FF}\\u{1E7E0}-\\u{1E7E6}\\u{1E7E8}-\\u{1E7EB}\\u{1E7ED}-\\u{1E7EE}\\u{1E7F0}-\\u{1E7FE}\\u{1E800}-\\u{1E8C4}\\u{1E900}-\\u{1E943}\\u{1E94B}\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}-\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}-\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}-\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u{20000}-\\u{2A6DF}\\u{2A700}-\\u{2B81D}\\u{2B820}-\\u{2CEAD}\\u{2CEB0}-\\u{2EBE0}\\u{2EBF0}-\\u{2EE5D}\\u{2F800}-\\u{2FA1D}\\u{30000}-\\u{3134A}\\u{31350}-\\u{33479}\\u0030-\\u0039\\u00B2-\\u00B3\\u00B9\\u00BC-\\u00BE\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u09F4-\\u09F9\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0B72-\\u0B77\\u0BE6-\\u0BF2\\u0C66-\\u0C6F\\u0C78-\\u0C7E\\u0CE6-\\u0CEF\\u0D58-\\u0D5E\\u0D66-\\u0D78\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F33\\u1040-\\u1049\\u1090-\\u1099\\u1369-\\u137C\\u16EE-\\u16F0\\u17E0-\\u17E9\\u17F0-\\u17F9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19DA\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\u2070\\u2074-\\u2079\\u2080-\\u2089\\u2150-\\u2182\\u2185-\\u2189\\u2460-\\u249B\\u24EA-\\u24FF\\u2776-\\u2793\\u2CFD\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u3192-\\u3195\\u3220-\\u3229\\u3248-\\u324F\\u3251-\\u325F\\u3280-\\u3289\\u32B1-\\u32BF\\uA620-\\uA629\\uA6E6-\\uA6EF\\uA830-\\uA835\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19\\u{10107}-\\u{10133}\\u{10140}-\\u{10178}\\u{1018A}-\\u{1018B}\\u{102E1}-\\u{102FB}\\u{10320}-\\u{10323}\\u{10341}\\u{1034A}\\u{103D1}-\\u{103D5}\\u{104A0}-\\u{104A9}\\u{10858}-\\u{1085F}\\u{10879}-\\u{1087F}\\u{108A7}-\\u{108AF}\\u{108FB}-\\u{108FF}\\u{10916}-\\u{1091B}\\u{109BC}-\\u{109BD}\\u{109C0}-\\u{109CF}\\u{109D2}-\\u{109FF}\\u{10A40}-\\u{10A48}\\u{10A7D}-\\u{10A7E}\\u{10A9D}-\\u{10A9F}\\u{10AEB}-\\u{10AEF}\\u{10B58}-\\u{10B5F}\\u{10B78}-\\u{10B7F}\\u{10BA9}-\\u{10BAF}\\u{10CFA}-\\u{10CFF}\\u{10D30}-\\u{10D39}\\u{10D40}-\\u{10D49}\\u{10E60}-\\u{10E7E}\\u{10F1D}-\\u{10F26}\\u{10F51}-\\u{10F54}\\u{10FC5}-\\u{10FCB}\\u{11052}-\\u{1106F}\\u{110F0}-\\u{110F9}\\u{11136}-\\u{1113F}\\u{111D0}-\\u{111D9}\\u{111E1}-\\u{111F4}\\u{112F0}-\\u{112F9}\\u{11450}-\\u{11459}\\u{114D0}-\\u{114D9}\\u{11650}-\\u{11659}\\u{116C0}-\\u{116C9}\\u{116D0}-\\u{116E3}\\u{11730}-\\u{1173B}\\u{118E0}-\\u{118F2}\\u{11950}-\\u{11959}\\u{11BF0}-\\u{11BF9}\\u{11C50}-\\u{11C6C}\\u{11D50}-\\u{11D59}\\u{11DA0}-\\u{11DA9}\\u{11DE0}-\\u{11DE9}\\u{11F50}-\\u{11F59}\\u{11FC0}-\\u{11FD4}\\u{12400}-\\u{1246E}\\u{16130}-\\u{16139}\\u{16A60}-\\u{16A69}\\u{16AC0}-\\u{16AC9}\\u{16B50}-\\u{16B59}\\u{16B5B}-\\u{16B61}\\u{16D70}-\\u{16D79}\\u{16E80}-\\u{16E96}\\u{16FF4}-\\u{16FF6}\\u{1CCF0}-\\u{1CCF9}\\u{1D2C0}-\\u{1D2D3}\\u{1D2E0}-\\u{1D2F3}\\u{1D360}-\\u{1D378}\\u{1D7CE}-\\u{1D7FF}\\u{1E140}-\\u{1E149}\\u{1E2F0}-\\u{1E2F9}\\u{1E4F0}-\\u{1E4F9}\\u{1E5F1}-\\u{1E5FA}\\u{1E8C7}-\\u{1E8CF}\\u{1E950}-\\u{1E959}\\u{1EC71}-\\u{1ECAB}\\u{1ECAD}-\\u{1ECAF}\\u{1ECB1}-\\u{1ECB4}\\u{1ED01}-\\u{1ED2D}\\u{1ED2F}-\\u{1ED3D}\\u{1F100}-\\u{1F10C}\\u{1FBF0}-\\u{1FBF9}]+", "u")).filter(Boolean).slice(0, DATASET_FILTER_WORDS_MAX);
586
- }
587
- /** A word's prefixes, 1 to {@link DATASET_FILTER_PREFIX_MAX} characters. */ function prefixes(word) {
588
- const chars = Array.from(word).slice(0, DATASET_FILTER_PREFIX_MAX);
589
- return chars.map((_char, at)=>chars.slice(0, at + 1).join(''));
590
- }
591
- /** A word clipped the way its stored prefixes are. */ function clipWord(word) {
592
- return Array.from(word).slice(0, DATASET_FILTER_PREFIX_MAX).join('');
593
- }
594
- const isEnum = (field)=>{
595
- var _ref;
596
- var _field_validation_options, _field_validation;
597
- return field.type === 'text' && ((_ref = (_field_validation = field.validation) == null ? void 0 : (_field_validation_options = _field_validation.options) == null ? void 0 : _field_validation_options.length) != null ? _ref : 0) > 0;
598
- };
599
- const isNumeric = (field)=>field.type === 'int32' || field.type === 'int64' || field.type === 'float';
600
- /**
601
- * A stored value as text, or null. Numbers are accepted because a form or an
602
- * automation stores every value as text and an import may do the reverse.
603
- */ function asText(value) {
604
- if (typeof value === 'string') return value;
605
- if (typeof value === 'number' && Number.isFinite(value)) return String(value);
606
- return null;
607
- }
608
- /**
609
- * Plain text as its stored key — its `=` token's value and its
610
- * `filterValues` entry: lower-cased, trimmed, clipped to
611
- * {@link DATASET_FILTER_VALUE_MAX} characters. What the records table asks a
612
- * typed value to equal.
613
- */ export function datasetFilterTextKey(value) {
614
- return Array.from(value.trim().toLowerCase()).slice(0, DATASET_FILTER_VALUE_MAX).join('');
615
- }
616
- const textKey = datasetFilterTextKey;
617
- /** A stored number (or its text form) as its `=` token's value. */ function numberKey(value) {
618
- const number = typeof value === 'number' ? value : typeof value === 'string' && value.trim() !== '' ? Number(value) : Number.NaN;
619
- return Number.isFinite(number) ? String(number) : null;
620
- }
621
- /** A stored boolean (or its text form) as its `=` token's value. */ function boolKey(value) {
622
- if (typeof value === 'boolean') return String(value);
623
- if (value === 'true' || value === 'false') return value;
624
- return null;
625
- }
626
- /** Every field a model declares, in display order and then the rest. */ function modelFieldIds(model) {
627
- var _model_order, _model_fields;
628
- return [
629
- ...new Set([
630
- ...(_model_order = model.order) != null ? _model_order : [],
631
- ...Object.keys((_model_fields = model.fields) != null ? _model_fields : {})
632
- ])
633
- ];
634
- }
635
- /** The tokens described in {@link DATASET_FILTER_KEYS_MAX}'s comment. */ export function datasetFilterKeys(model, values) {
636
- const exact = [];
637
- const fieldWords = [];
638
- const search = [];
639
- const searchable = (text)=>{
640
- for (const word of datasetFilterWords(text)){
641
- for (const prefix of prefixes(word))search.push(`s:${prefix}`);
642
- }
643
- };
644
- for (const fieldId of modelFieldIds(model)){
645
- var _model_fields;
646
- const field = (_model_fields = model.fields) == null ? void 0 : _model_fields[fieldId];
647
- const stored = values == null ? void 0 : values[fieldId];
648
- if (!field || stored == null) continue;
649
- if (field.type === 'text') {
650
- const text = asText(stored);
651
- if (text == null || !text.trim()) continue;
652
- if (isEnum(field)) {
653
- exact.push(`f:${fieldId}=${text}`);
654
- } else {
655
- exact.push(`f:${fieldId}=${textKey(text)}`);
656
- for (const word of datasetFilterWords(text)){
657
- for (const prefix of prefixes(word)){
658
- fieldWords.push(`f:${fieldId}^${prefix}`);
659
- }
660
- }
661
- }
662
- searchable(text);
663
- } else if (field.type === 'bool') {
664
- const key = boolKey(stored);
665
- if (key) exact.push(`f:${fieldId}=${key}`);
666
- } else if (isNumeric(field)) {
667
- const key = numberKey(stored);
668
- if (key) exact.push(`f:${fieldId}=${key}`);
669
- } else if (field.type === 'sorted') {
670
- for (const member of Array.isArray(stored) ? stored : [
671
- stored
672
- ]){
673
- var _asText;
674
- const text = (_asText = asText(member)) == null ? void 0 : _asText.trim();
675
- if (!text) continue;
676
- exact.push(`f:${fieldId}=${text}`);
677
- searchable(text);
678
- }
679
- }
680
- }
681
- const kept = [
682
- ...new Set([
683
- ...exact,
684
- ...fieldWords,
685
- ...search
686
- ])
687
- ].slice(0, DATASET_FILTER_KEYS_MAX);
688
- return kept.sort();
689
- }
690
- /**
691
- * Field ids a query can name as `filterValues.<id>`. A path string splits on
692
- * `.`, and the SDKs refuse `~ * / [ ]` in one; the model editor allows only
693
- * letters, digits and underscores, but a v1 dataset's column names were
694
- * never checked. Backticks are refused too, as the escape a path would need.
695
- */ const FILTER_VALUE_ID = /^[^.~*/[\]`]+$/;
696
- /**
697
- * The query path of a field's `filterValues` entry, or null for a field id
698
- * no path can name — such a field is stamped no entry, and the records table
699
- * offers it no equality (its `contains`, served by `filterKeys`, stands).
700
- */ export function datasetFilterValuePath(fieldId) {
701
- return FILTER_VALUE_ID.test(fieldId) && !/^__.*__$/.test(fieldId) ? `filterValues.${fieldId}` : null;
702
- }
703
- /**
704
- * A dataset record's FILTER VALUES: `{ [fieldId]: value }`, one scalar per
705
- * field an equality can be asked of, so the records table puts every
706
- * equality on its query (AGL-3321).
707
- *
708
- * enum text (`validation.options`) the value exactly, as its `=` token;
709
- * plain text {@link datasetFilterTextKey};
710
- * bool a boolean;
711
- * int32 / int64 / float a number.
712
- *
713
- * A form or an automation stores every value as text, so `'19.50'` is the
714
- * number 19.5 and `'true'` the boolean, as in {@link datasetFilterKeys}.
715
- * Fields with no value, and timestamps, lists, references, maps, bytes,
716
- * coordinates and nil fields, carry no entry: a list is asked by member
717
- * (`f:<id>=<member>` in `filterKeys`), and the rest are not asked about by
718
- * value.
719
- *
720
- * ## What it costs
721
- *
722
- * Unlike `values`, this map is NOT exempt from indexing — that is its whole
723
- * point. Each entry is a scalar and costs two automatic index entries
724
- * (ascending, descending), so a record costs at most two per model field —
725
- * bounded by the model's field count (the `/v1` API caps a model at 100
726
- * fields), far inside Firestore's 40,000 entries per document, where
727
- * `values` was unbounded because a `map` or `sorted` value may hold anything.
728
- */ export function datasetFilterValues(model, values) {
729
- const out = {};
730
- for (const fieldId of modelFieldIds(model)){
731
- var _model_fields;
732
- const field = (_model_fields = model.fields) == null ? void 0 : _model_fields[fieldId];
733
- const stored = values == null ? void 0 : values[fieldId];
734
- if (!field || stored == null || !datasetFilterValuePath(fieldId)) continue;
735
- if (field.type === 'text') {
736
- const text = asText(stored);
737
- if (text == null || !text.trim()) continue;
738
- out[fieldId] = isEnum(field) ? text : textKey(text);
739
- } else if (field.type === 'bool') {
740
- const key = boolKey(stored);
741
- if (key) out[fieldId] = key === 'true';
742
- } else if (isNumeric(field)) {
743
- const key = numberKey(stored);
744
- if (key) out[fieldId] = Number(key);
745
- }
746
- }
747
- return out;
748
- }
749
- /**
750
- * The `filterKeys` token a word-level clause asks for, or null when it is
751
- * not one:
752
- *
753
- * - plain text `contains` → the `^` token of the value's FIRST word, so a
754
- * `contains` is a word-prefix match, not a mid-string one, and one word
755
- * of several is asked (the records table says so);
756
- * - list `contains` (and `equals`) → the member's `=` token;
757
- * - enum, boolean, number and plain text `equals` → the `=` token of the
758
- * value. The records table asks these of `filterValues` instead, which
759
- * composes with a `contains`; the token remains what the quick search and
760
- * older readers match.
761
- * - everything else — ranges, dates, `isEmpty`, `isAnyOf`, negations — null.
762
- *
763
- * `clause.field` is the field id. The operator names are the grid's: a
764
- * select's `equals`, a number's `=`, a boolean's `is`, a list's `contains`.
765
- */ export function datasetFilterToken(model, clause) {
766
- var _clause_value;
767
- var _model_fields;
768
- const field = (_model_fields = model.fields) == null ? void 0 : _model_fields[clause.field];
769
- if (!field) return null;
770
- const { op } = clause;
771
- const value = String((_clause_value = clause.value) != null ? _clause_value : '');
772
- if (!value.trim()) return null;
773
- const equals = op === 'equals' || op === '=' || op === 'is';
774
- if (field.type === 'text') {
775
- if (isEnum(field)) return equals ? `f:${clause.field}=${value}` : null;
776
- if (equals) return `f:${clause.field}=${textKey(value)}`;
777
- if (op === 'contains') {
778
- const [first] = datasetFilterWords(value);
779
- return first ? `f:${clause.field}^${clipWord(first)}` : null;
780
- }
781
- return null;
782
- }
783
- if (field.type === 'bool') {
784
- const key = boolKey(value.trim());
785
- return equals && key ? `f:${clause.field}=${key}` : null;
786
- }
787
- if (isNumeric(field)) {
788
- const key = numberKey(value);
789
- return equals && key ? `f:${clause.field}=${key}` : null;
790
- }
791
- if (field.type === 'sorted') {
792
- return equals || op === 'contains' ? `f:${clause.field}=${value.trim()}` : null;
793
- }
794
- return null;
795
- }
796
- /**
797
- * The quick-search token for one typed word, or null for a word with no
798
- * letters or digits. A word with punctuation inside it is asked by its first
799
- * run — split the search text with {@link datasetFilterWords} to ask for
800
- * every run.
801
- */ export function datasetSearchToken(word) {
802
- const [first] = datasetFilterWords(word);
803
- return first ? `s:${clipWord(first)}` : null;
804
- }
805
429
 
806
430
  //# sourceMappingURL=dataset-models.js.map