@graphty/graph-format 0.0.0 → 0.2.0

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.
Files changed (224) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +216 -43
  3. package/dist/graph-format.d.ts +1 -0
  4. package/dist/graph-format.d.ts.map +1 -0
  5. package/dist/graph-format.js +15798 -0
  6. package/dist/graph-format.js.map +1 -0
  7. package/dist/src/builder/arena.d.ts +85 -0
  8. package/dist/src/builder/arena.d.ts.map +1 -0
  9. package/dist/src/builder/arena.js +154 -0
  10. package/dist/src/builder/arena.js.map +1 -0
  11. package/dist/src/builder/compact.d.ts +515 -0
  12. package/dist/src/builder/compact.d.ts.map +1 -0
  13. package/dist/src/builder/compact.js +1553 -0
  14. package/dist/src/builder/compact.js.map +1 -0
  15. package/dist/src/builder/counting-sort.d.ts +96 -0
  16. package/dist/src/builder/counting-sort.d.ts.map +1 -0
  17. package/dist/src/builder/counting-sort.js +430 -0
  18. package/dist/src/builder/counting-sort.js.map +1 -0
  19. package/dist/src/builder/freeze.d.ts +72 -0
  20. package/dist/src/builder/freeze.d.ts.map +1 -0
  21. package/dist/src/builder/freeze.js +548 -0
  22. package/dist/src/builder/freeze.js.map +1 -0
  23. package/dist/src/builder/graph-builder.d.ts +602 -0
  24. package/dist/src/builder/graph-builder.d.ts.map +1 -0
  25. package/dist/src/builder/graph-builder.js +1940 -0
  26. package/dist/src/builder/graph-builder.js.map +1 -0
  27. package/dist/src/builder/options.d.ts +16 -0
  28. package/dist/src/builder/options.d.ts.map +1 -0
  29. package/dist/src/builder/options.js +21 -0
  30. package/dist/src/builder/options.js.map +1 -0
  31. package/dist/src/columns/bitmap.d.ts +96 -0
  32. package/dist/src/columns/bitmap.d.ts.map +1 -0
  33. package/dist/src/columns/bitmap.js +184 -0
  34. package/dist/src/columns/bitmap.js.map +1 -0
  35. package/dist/src/columns/column.d.ts +220 -0
  36. package/dist/src/columns/column.d.ts.map +1 -0
  37. package/dist/src/columns/column.js +2109 -0
  38. package/dist/src/columns/column.js.map +1 -0
  39. package/dist/src/columns/dictionary.d.ts +60 -0
  40. package/dist/src/columns/dictionary.d.ts.map +1 -0
  41. package/dist/src/columns/dictionary.js +102 -0
  42. package/dist/src/columns/dictionary.js.map +1 -0
  43. package/dist/src/columns/growable.d.ts +198 -0
  44. package/dist/src/columns/growable.d.ts.map +1 -0
  45. package/dist/src/columns/growable.js +296 -0
  46. package/dist/src/columns/growable.js.map +1 -0
  47. package/dist/src/columns/infer.d.ts +121 -0
  48. package/dist/src/columns/infer.d.ts.map +1 -0
  49. package/dist/src/columns/infer.js +268 -0
  50. package/dist/src/columns/infer.js.map +1 -0
  51. package/dist/src/columns/remap.d.ts +143 -0
  52. package/dist/src/columns/remap.d.ts.map +1 -0
  53. package/dist/src/columns/remap.js +588 -0
  54. package/dist/src/columns/remap.js.map +1 -0
  55. package/dist/src/columns/table.d.ts +184 -0
  56. package/dist/src/columns/table.d.ts.map +1 -0
  57. package/dist/src/columns/table.js +485 -0
  58. package/dist/src/columns/table.js.map +1 -0
  59. package/dist/src/constants.d.ts +34 -0
  60. package/dist/src/constants.d.ts.map +1 -0
  61. package/dist/src/constants.js +79 -0
  62. package/dist/src/constants.js.map +1 -0
  63. package/dist/src/errors.d.ts +76 -0
  64. package/dist/src/errors.d.ts.map +1 -0
  65. package/dist/src/errors.js +32 -0
  66. package/dist/src/errors.js.map +1 -0
  67. package/dist/src/ids/edge-id-index.d.ts +77 -0
  68. package/dist/src/ids/edge-id-index.d.ts.map +1 -0
  69. package/dist/src/ids/edge-id-index.js +155 -0
  70. package/dist/src/ids/edge-id-index.js.map +1 -0
  71. package/dist/src/ids/node-id-map.d.ts +280 -0
  72. package/dist/src/ids/node-id-map.d.ts.map +1 -0
  73. package/dist/src/ids/node-id-map.js +1139 -0
  74. package/dist/src/ids/node-id-map.js.map +1 -0
  75. package/dist/src/ids/string-store.d.ts +219 -0
  76. package/dist/src/ids/string-store.d.ts.map +1 -0
  77. package/dist/src/ids/string-store.js +526 -0
  78. package/dist/src/ids/string-store.js.map +1 -0
  79. package/dist/src/index.d.ts +30 -0
  80. package/dist/src/index.d.ts.map +1 -0
  81. package/dist/src/index.js +29 -0
  82. package/dist/src/index.js.map +1 -0
  83. package/dist/src/populate/from-csr.d.ts +61 -0
  84. package/dist/src/populate/from-csr.d.ts.map +1 -0
  85. package/dist/src/populate/from-csr.js +706 -0
  86. package/dist/src/populate/from-csr.js.map +1 -0
  87. package/dist/src/populate/from-edge-arrays.d.ts +51 -0
  88. package/dist/src/populate/from-edge-arrays.d.ts.map +1 -0
  89. package/dist/src/populate/from-edge-arrays.js +217 -0
  90. package/dist/src/populate/from-edge-arrays.js.map +1 -0
  91. package/dist/src/populate/from-records.d.ts +51 -0
  92. package/dist/src/populate/from-records.d.ts.map +1 -0
  93. package/dist/src/populate/from-records.js +447 -0
  94. package/dist/src/populate/from-records.js.map +1 -0
  95. package/dist/src/snapshot/derived.d.ts +211 -0
  96. package/dist/src/snapshot/derived.d.ts.map +1 -0
  97. package/dist/src/snapshot/derived.js +1254 -0
  98. package/dist/src/snapshot/derived.js.map +1 -0
  99. package/dist/src/snapshot/graph-meta.d.ts +23 -0
  100. package/dist/src/snapshot/graph-meta.d.ts.map +1 -0
  101. package/dist/src/snapshot/graph-meta.js +159 -0
  102. package/dist/src/snapshot/graph-meta.js.map +1 -0
  103. package/dist/src/snapshot/graph-snapshot.d.ts +509 -0
  104. package/dist/src/snapshot/graph-snapshot.d.ts.map +1 -0
  105. package/dist/src/snapshot/graph-snapshot.js +1247 -0
  106. package/dist/src/snapshot/graph-snapshot.js.map +1 -0
  107. package/dist/src/snapshot/hash.d.ts +90 -0
  108. package/dist/src/snapshot/hash.d.ts.map +1 -0
  109. package/dist/src/snapshot/hash.js +228 -0
  110. package/dist/src/snapshot/hash.js.map +1 -0
  111. package/dist/src/snapshot/queries.d.ts +74 -0
  112. package/dist/src/snapshot/queries.d.ts.map +1 -0
  113. package/dist/src/snapshot/queries.js +131 -0
  114. package/dist/src/snapshot/queries.js.map +1 -0
  115. package/dist/src/snapshot/validate.d.ts +192 -0
  116. package/dist/src/snapshot/validate.d.ts.map +1 -0
  117. package/dist/src/snapshot/validate.js +990 -0
  118. package/dist/src/snapshot/validate.js.map +1 -0
  119. package/dist/src/snapshot/views.d.ts +274 -0
  120. package/dist/src/snapshot/views.d.ts.map +1 -0
  121. package/dist/src/snapshot/views.js +856 -0
  122. package/dist/src/snapshot/views.js.map +1 -0
  123. package/dist/src/types/builder.d.ts +456 -0
  124. package/dist/src/types/builder.d.ts.map +1 -0
  125. package/dist/src/types/builder.js +11 -0
  126. package/dist/src/types/builder.js.map +1 -0
  127. package/dist/src/types/columns.d.ts +615 -0
  128. package/dist/src/types/columns.d.ts.map +1 -0
  129. package/dist/src/types/columns.js +14 -0
  130. package/dist/src/types/columns.js.map +1 -0
  131. package/dist/src/types/index.d.ts +14 -0
  132. package/dist/src/types/index.d.ts.map +1 -0
  133. package/dist/src/types/index.js +11 -0
  134. package/dist/src/types/index.js.map +1 -0
  135. package/dist/src/types/internal.d.ts +172 -0
  136. package/dist/src/types/internal.d.ts.map +1 -0
  137. package/dist/src/types/internal.js +13 -0
  138. package/dist/src/types/internal.js.map +1 -0
  139. package/dist/src/types/snapshot.d.ts +801 -0
  140. package/dist/src/types/snapshot.d.ts.map +1 -0
  141. package/dist/src/types/snapshot.js +14 -0
  142. package/dist/src/types/snapshot.js.map +1 -0
  143. package/dist/src/types/wire.d.ts +196 -0
  144. package/dist/src/types/wire.d.ts.map +1 -0
  145. package/dist/src/types/wire.js +8 -0
  146. package/dist/src/types/wire.js.map +1 -0
  147. package/dist/src/util/mask.d.ts +55 -0
  148. package/dist/src/util/mask.d.ts.map +1 -0
  149. package/dist/src/util/mask.js +71 -0
  150. package/dist/src/util/mask.js.map +1 -0
  151. package/dist/src/util/options.d.ts +22 -0
  152. package/dist/src/util/options.d.ts.map +1 -0
  153. package/dist/src/util/options.js +49 -0
  154. package/dist/src/util/options.js.map +1 -0
  155. package/dist/src/util/shared-buffers.d.ts +42 -0
  156. package/dist/src/util/shared-buffers.d.ts.map +1 -0
  157. package/dist/src/util/shared-buffers.js +52 -0
  158. package/dist/src/util/shared-buffers.js.map +1 -0
  159. package/dist/src/util/typed-array.d.ts +205 -0
  160. package/dist/src/util/typed-array.d.ts.map +1 -0
  161. package/dist/src/util/typed-array.js +289 -0
  162. package/dist/src/util/typed-array.js.map +1 -0
  163. package/dist/src/wire/bytes.d.ts +69 -0
  164. package/dist/src/wire/bytes.d.ts.map +1 -0
  165. package/dist/src/wire/bytes.js +361 -0
  166. package/dist/src/wire/bytes.js.map +1 -0
  167. package/dist/src/wire/carried-views.d.ts +111 -0
  168. package/dist/src/wire/carried-views.d.ts.map +1 -0
  169. package/dist/src/wire/carried-views.js +286 -0
  170. package/dist/src/wire/carried-views.js.map +1 -0
  171. package/dist/src/wire/from-wire.d.ts +144 -0
  172. package/dist/src/wire/from-wire.d.ts.map +1 -0
  173. package/dist/src/wire/from-wire.js +1425 -0
  174. package/dist/src/wire/from-wire.js.map +1 -0
  175. package/dist/src/wire/to-wire.d.ts +171 -0
  176. package/dist/src/wire/to-wire.d.ts.map +1 -0
  177. package/dist/src/wire/to-wire.js +652 -0
  178. package/dist/src/wire/to-wire.js.map +1 -0
  179. package/dist/tsconfig.build.tsbuildinfo +1 -0
  180. package/package.json +72 -7
  181. package/src/builder/arena.ts +192 -0
  182. package/src/builder/compact.ts +1811 -0
  183. package/src/builder/counting-sort.ts +506 -0
  184. package/src/builder/freeze.ts +689 -0
  185. package/src/builder/graph-builder.ts +2264 -0
  186. package/src/builder/options.ts +29 -0
  187. package/src/columns/bitmap.ts +196 -0
  188. package/src/columns/column.ts +2574 -0
  189. package/src/columns/dictionary.ts +114 -0
  190. package/src/columns/growable.ts +350 -0
  191. package/src/columns/infer.ts +285 -0
  192. package/src/columns/remap.ts +663 -0
  193. package/src/columns/table.ts +568 -0
  194. package/src/constants.ts +89 -0
  195. package/src/errors.ts +115 -0
  196. package/src/ids/edge-id-index.ts +179 -0
  197. package/src/ids/node-id-map.ts +1340 -0
  198. package/src/ids/string-store.ts +589 -0
  199. package/src/index.ts +139 -0
  200. package/src/lib-resizable-array-buffer.d.ts +34 -0
  201. package/src/populate/from-csr.ts +802 -0
  202. package/src/populate/from-edge-arrays.ts +257 -0
  203. package/src/populate/from-records.ts +536 -0
  204. package/src/snapshot/derived.ts +1486 -0
  205. package/src/snapshot/graph-meta.ts +167 -0
  206. package/src/snapshot/graph-snapshot.ts +1503 -0
  207. package/src/snapshot/hash.ts +241 -0
  208. package/src/snapshot/queries.ts +136 -0
  209. package/src/snapshot/validate.ts +1108 -0
  210. package/src/snapshot/views.ts +943 -0
  211. package/src/types/builder.ts +480 -0
  212. package/src/types/columns.ts +727 -0
  213. package/src/types/index.ts +14 -0
  214. package/src/types/internal.ts +195 -0
  215. package/src/types/snapshot.ts +886 -0
  216. package/src/types/wire.ts +207 -0
  217. package/src/util/mask.ts +89 -0
  218. package/src/util/options.ts +57 -0
  219. package/src/util/shared-buffers.ts +56 -0
  220. package/src/util/typed-array.ts +371 -0
  221. package/src/wire/bytes.ts +440 -0
  222. package/src/wire/carried-views.ts +327 -0
  223. package/src/wire/from-wire.ts +1690 -0
  224. package/src/wire/to-wire.ts +755 -0
@@ -0,0 +1,285 @@
1
+ /**
2
+ * Type inference for untyped sources (design section 5.1, decision C4): CSV cells, GML values, DOT
3
+ * strings, `fromRecords` and `addNodeRecord`. Inference runs per COLUMN, never per cell, widening
4
+ * monotonically in the order `(unset) -> bool -> i32 -> f64 -> string -> json`; it never yields
5
+ * `f32`. The lexical grammar for text is fixed so two implementations agree (invariant I15):
6
+ *
7
+ * - `bool` is exactly `true` / `false` (case-sensitive);
8
+ * - `i32` is `/^-?(0|[1-9][0-9]*)$/` within `[-2^31, 2^31)`;
9
+ * - `f64` is a decimal or exponent literal accepted by `Number()` that is not empty, not whitespace,
10
+ * not `Infinity` / `NaN` and not a hex / octal / binary form (and whose value is finite);
11
+ * - everything else is `string`. `0` / `1` text is `i32`, never `bool`.
12
+ *
13
+ * JS values map by `typeof`: `boolean -> bool`, integral `number` in i32 range -> `i32`, other
14
+ * `number -> f64`, `string -> string`, array or plain object -> `json`; `null` and `undefined` are
15
+ * unset and do not widen.
16
+ */
17
+
18
+ import { GraphFormatError } from "../errors.js";
19
+
20
+ /** The dtypes inference can produce, in widening order. */
21
+ export type InferredDtype = "bool" | "i32" | "f64" | "string" | "json";
22
+
23
+ /** The widening order of design section 5.1; a column only ever moves to the right. */
24
+ export const WIDENING_ORDER: readonly InferredDtype[] = ["bool", "i32", "f64", "string", "json"];
25
+
26
+ const I32_TEXT = /^-?(0|[1-9][0-9]*)$/;
27
+ // integer part without leading zeros ("01" is a string, design section 5.1), optional fraction, optional exponent
28
+ const F64_TEXT = /^[+-]?((0|[1-9][0-9]*)(\.[0-9]*)?|\.[0-9]+)([eE][+-]?[0-9]+)?$/;
29
+ const I32_MIN = -2147483648;
30
+ const I32_MAX = 2147483647;
31
+
32
+ /**
33
+ * The rank of a dtype in the widening order.
34
+ * @param dtype - an inferred dtype
35
+ * @returns 0 for bool up to 4 for json
36
+ */
37
+ export function wideningRank(dtype: InferredDtype): number {
38
+ return WIDENING_ORDER.indexOf(dtype);
39
+ }
40
+
41
+ /**
42
+ * Widen a column's dtype by one observation: the wider of the two, where null (unset, nothing seen
43
+ * yet) is narrower than everything.
44
+ * @param current - the column's dtype so far, or null when no value has been seen
45
+ * @param observed - the dtype of the newly observed value, or null for an unset cell
46
+ * @returns the widened dtype (null only when both are null)
47
+ */
48
+ export function widenDtype(current: InferredDtype | null, observed: InferredDtype | null): InferredDtype | null {
49
+ if (current === null) {
50
+ return observed;
51
+ }
52
+ if (observed === null) {
53
+ return current;
54
+ }
55
+ return wideningRank(observed) > wideningRank(current) ? observed : current;
56
+ }
57
+
58
+ /**
59
+ * Classify one text cell by the fixed lexical grammar. Exported as the reference implementation of
60
+ * the text grammar of design section 5.1, which the text importers of graph-io must reproduce
61
+ * exactly (invariant I15); the core itself only infers from JS values, so nothing in src/ calls it.
62
+ * @public
63
+ * @param text - the cell text, exactly as read (no trimming)
64
+ * @returns bool, i32, f64 or string
65
+ */
66
+ export function inferTextDtype(text: string): "bool" | "i32" | "f64" | "string" {
67
+ if (text === "true" || text === "false") {
68
+ return "bool";
69
+ }
70
+ if (I32_TEXT.test(text)) {
71
+ const n = Number(text);
72
+ if (n >= I32_MIN && n <= I32_MAX) {
73
+ return "i32";
74
+ }
75
+ return Number.isFinite(n) ? "f64" : "string";
76
+ }
77
+ if (F64_TEXT.test(text) && Number.isFinite(Number(text))) {
78
+ return "f64";
79
+ }
80
+ return "string";
81
+ }
82
+
83
+ /**
84
+ * Classify one JS value by `typeof`.
85
+ * @param value - the cell value
86
+ * @returns the dtype, or null for an unset cell (`undefined` or `null`); E_COLUMN_TYPE for a bigint,
87
+ * symbol or function, which no dtype can hold
88
+ */
89
+ export function inferValueDtype(value: unknown): InferredDtype | null {
90
+ if (value === undefined || value === null) {
91
+ return null;
92
+ }
93
+ switch (typeof value) {
94
+ case "boolean":
95
+ return "bool";
96
+ case "number":
97
+ return Number.isInteger(value) && value >= I32_MIN && value <= I32_MAX ? "i32" : "f64";
98
+ case "string":
99
+ return "string";
100
+ case "object":
101
+ return "json";
102
+ default:
103
+ throw new GraphFormatError("E_COLUMN_TYPE", `a ${typeof value} value cannot be stored in a column`, {
104
+ reason: "unsupported value type",
105
+ found: typeof value,
106
+ });
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Parse a text cell into the storage value of a dtype the column has widened to. A text column
112
+ * never widens to json, but a column that was inferred from JS values may later receive text; in
113
+ * that case the text is stored as a string value. Part of the reference text grammar of design
114
+ * section 5.1 (see inferTextDtype).
115
+ * @public
116
+ * @param text - the cell text
117
+ * @param dtype - the column's current dtype
118
+ * @returns a boolean for bool, a number for i32 / f64, the text itself for string / json
119
+ */
120
+ export function parseText(text: string, dtype: InferredDtype): boolean | number | string {
121
+ switch (dtype) {
122
+ case "bool":
123
+ if (text === "true") {
124
+ return true;
125
+ }
126
+ if (text === "false") {
127
+ return false;
128
+ }
129
+ throw new GraphFormatError("E_COLUMN_TYPE", `text "${text}" is not a bool`, { value: text, dtype });
130
+ case "i32":
131
+ case "f64": {
132
+ const inferred = inferTextDtype(text);
133
+ if (inferred === "string") {
134
+ throw new GraphFormatError("E_COLUMN_TYPE", `text "${text}" is not a ${dtype}`, { value: text, dtype });
135
+ }
136
+ if (inferred === "bool") {
137
+ return text === "true" ? 1 : 0;
138
+ }
139
+ return Number(text);
140
+ }
141
+ case "string":
142
+ case "json":
143
+ return text;
144
+ default: {
145
+ const name: string = dtype;
146
+ throw new GraphFormatError("E_COLUMN_TYPE", `unknown dtype ${name}`, { dtype: name });
147
+ }
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Convert a JS value to the storage value of a (possibly wider) inferred dtype, applying the
153
+ * widening conversions of design section 5.1: a bool widened to a number becomes 1 / 0, a number or
154
+ * boolean widened to string becomes its canonical JS text, anything widened to json is kept as is.
155
+ * @param value - the cell value (never undefined or null; unset cells are handled by the caller)
156
+ * @param dtype - the column's dtype
157
+ * @returns the storage value; E_COLUMN_TYPE when the value cannot be represented in the dtype
158
+ */
159
+ export function coerceValue(value: unknown, dtype: InferredDtype): unknown {
160
+ switch (dtype) {
161
+ case "bool":
162
+ if (typeof value === "boolean") {
163
+ return value;
164
+ }
165
+ break;
166
+ case "i32":
167
+ case "f64":
168
+ if (typeof value === "number") {
169
+ return value;
170
+ }
171
+ if (typeof value === "boolean") {
172
+ return value ? 1 : 0;
173
+ }
174
+ break;
175
+ case "string":
176
+ if (typeof value === "string") {
177
+ return value;
178
+ }
179
+ if (typeof value === "number" || typeof value === "boolean") {
180
+ return String(value);
181
+ }
182
+ break;
183
+ case "json":
184
+ return value;
185
+ default: {
186
+ const name: string = dtype;
187
+ throw new GraphFormatError("E_COLUMN_TYPE", `unknown dtype ${name}`, { dtype: name });
188
+ }
189
+ }
190
+ throw new GraphFormatError("E_COLUMN_TYPE", `a ${typeof value} value cannot be stored in a ${dtype} column`, {
191
+ dtype,
192
+ found: typeof value,
193
+ });
194
+ }
195
+
196
+ /**
197
+ * Infer the dtype of a whole column of JS values in one pass.
198
+ * @param values - the cell values; undefined and null are unset
199
+ * @returns the widened dtype, or null when every cell is unset
200
+ */
201
+ export function inferValuesDtype(values: Iterable<unknown>): InferredDtype | null {
202
+ let dtype: InferredDtype | null = null;
203
+ for (const value of values) {
204
+ dtype = widenDtype(dtype, inferValueDtype(value));
205
+ if (dtype === "json") {
206
+ break;
207
+ }
208
+ }
209
+ return dtype;
210
+ }
211
+
212
+ /**
213
+ * Infer the dtype of a whole column of text cells in one pass. Part of the reference text grammar
214
+ * of design section 5.1 (see inferTextDtype).
215
+ * @public
216
+ * @param cells - the cell texts; an empty string is a string value, not an unset cell (the caller
217
+ * decides how blanks map to unset before calling)
218
+ * @returns the widened dtype, or null when the column is empty
219
+ */
220
+ export function inferTextsDtype(cells: Iterable<string>): InferredDtype | null {
221
+ let dtype: InferredDtype | null = null;
222
+ for (const text of cells) {
223
+ dtype = widenDtype(dtype, inferTextDtype(text));
224
+ if (dtype === "string") {
225
+ break;
226
+ }
227
+ }
228
+ return dtype;
229
+ }
230
+
231
+ /**
232
+ * A per-column inference accumulator for row-at-a-time sources (`addNodeRecord`, CSV streaming):
233
+ * observe every cell and read `dtype`; each observe call reports whether the column widened so the
234
+ * caller can reallocate storage and record the widening in FreezeReport.widened.
235
+ */
236
+ export class DtypeInferrer {
237
+ private current: InferredDtype | null = null;
238
+
239
+ /**
240
+ * The column's dtype so far.
241
+ * @returns the widened dtype, or null until a set cell has been observed
242
+ */
243
+ get dtype(): InferredDtype | null {
244
+ return this.current;
245
+ }
246
+
247
+ /**
248
+ * Observe a JS value.
249
+ * @param value - the cell value; undefined and null are unset and never widen
250
+ * @returns true when the column's dtype changed
251
+ */
252
+ observeValue(value: unknown): boolean {
253
+ return this.apply(inferValueDtype(value));
254
+ }
255
+
256
+ /**
257
+ * Observe a text cell. Part of the reference text grammar of design section 5.1 (see
258
+ * inferTextDtype).
259
+ * @public
260
+ * @param text - the cell text
261
+ * @returns true when the column's dtype changed
262
+ */
263
+ observeText(text: string): boolean {
264
+ return this.apply(inferTextDtype(text));
265
+ }
266
+
267
+ /**
268
+ * Force the dtype to at least the given one (a declared lower bound, or the dtype of a column
269
+ * being merged in).
270
+ * @param dtype - the dtype to widen to
271
+ * @returns true when the column's dtype changed
272
+ */
273
+ widenTo(dtype: InferredDtype): boolean {
274
+ return this.apply(dtype);
275
+ }
276
+
277
+ private apply(observed: InferredDtype | null): boolean {
278
+ const next = widenDtype(this.current, observed);
279
+ if (next === this.current) {
280
+ return false;
281
+ }
282
+ this.current = next;
283
+ return true;
284
+ }
285
+ }