@ai-matrx/records-ui 0.110.5 → 0.110.6

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog — @ai-matrx/records-ui
2
2
 
3
+ ## 0.110.6
4
+
5
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
6
+ `git diff npm/records-ui/v0.110.5..npm/records-ui/v0.110.6 -- apps/shared/records-ui`).
7
+ No source changes intended and no consumer action required.
8
+
3
9
  ## 0.110.5
4
10
 
5
11
  Automatic changed-only republish (docs/metadata drift since the last tag — see
@@ -2,14 +2,23 @@
2
2
 
3
3
  var core = require('@ai-matrx/records/core');
4
4
  var uuid = require('@ai-matrx/kit/uuid');
5
+ var textCase = require('@ai-matrx/kit/text-case');
5
6
  var records = require('@ai-matrx/records');
6
- require('@ai-matrx/kit/text-case');
7
- require('@ai-matrx/design-system/data-table');
7
+ var dataTable = require('@ai-matrx/design-system/data-table');
8
+ require('react');
8
9
  var tableStyle = require('@ai-matrx/design-system/data-table/table-style');
9
10
  require('@ai-matrx/design-system/field-formats');
10
11
  var realtime = require('@ai-matrx/realtime');
11
12
 
12
13
  // src/firstPage.ts
14
+ function fieldName(field2) {
15
+ const label = (field2.label ?? "").trim();
16
+ if (label !== "" && label === field2.key && /^[a-z0-9]+(?:[_.-][a-z0-9]+)+$/.test(label)) return textCase.humanizeIdentifier(label);
17
+ if (label !== "") return label;
18
+ const name = (field2.name ?? "").trim();
19
+ if (name !== "") return name;
20
+ return textCase.humanizeIdentifier(field2.key);
21
+ }
13
22
  var kernelId = (name) => records.KERNEL_TABLES.find((t) => t.name === name)?.id ?? "";
14
23
  var KERNEL_FILE_TABLE = kernelId("File");
15
24
  var KERNEL_PERSON_TABLE = kernelId("Person");
@@ -17,8 +26,8 @@ Object.fromEntries(
17
26
  records.PARITY_FIELD_TYPES.map((p) => [p.parity_type, p.made_of])
18
27
  );
19
28
  var KERNEL = { file: KERNEL_FILE_TABLE, person: KERNEL_PERSON_TABLE };
20
- function editorKindFor(field) {
21
- return records.fieldKindFor(field, KERNEL);
29
+ function editorKindFor(field2) {
30
+ return records.fieldKindFor(field2, KERNEL);
22
31
  }
23
32
 
24
33
  // src/gridLook.ts
@@ -45,8 +54,8 @@ function defaultSortOf(table, fields) {
45
54
  if (String(entry["direction"] ?? "").toLowerCase() === "desc") direction = "desc";
46
55
  }
47
56
  if (!name) return null;
48
- const field = fields.find((f) => f.key === name) ?? fields.find((f) => f.id === name);
49
- return field ? { id: field.key, direction } : null;
57
+ const field2 = fields.find((f) => f.key === name) ?? fields.find((f) => f.id === name);
58
+ return field2 ? { id: field2.key, direction } : null;
50
59
  }
51
60
  var MEASURE_LABEL = {
52
61
  count: "Count",
@@ -62,22 +71,169 @@ var MEASURE_LABEL = {
62
71
  percent_filled: "Percent filled",
63
72
  range: "Range"
64
73
  };
65
- function holdsNumbers(field) {
66
- const kind = String(editorKindFor(field));
74
+
75
+ // src/gridPage.ts
76
+ function filterRefusal(f) {
77
+ if (!computedOnRead(f)) return null;
78
+ return `${fieldName(f)} is worked out each time it is read, so the store keeps no value to filter the whole table by. Filter by a column it is worked out from.`;
79
+ }
80
+ function computedOnRead(f) {
81
+ const kind = String(editorKindFor(f));
82
+ return (f.type === "formula" || kind === "formula" || kind === "lookup" || kind === "rollup") && String(f.compute_on ?? "read") === "read";
83
+ }
84
+ function holdsNumbers(field2) {
85
+ const kind = String(editorKindFor(field2));
67
86
  if (kind === "number" || kind === "currency" || kind === "percent") return true;
68
- return String((field.config ?? {})["system"] ?? "") === "autonumber";
87
+ return String((field2.config ?? {})["system"] ?? "") === "autonumber";
69
88
  }
70
- function sortAsFor(field) {
71
- const kind = String(editorKindFor(field));
72
- if (holdsNumbers(field)) return "number";
89
+ function sortAsFor(field2) {
90
+ const kind = String(editorKindFor(field2));
91
+ if (holdsNumbers(field2)) return "number";
73
92
  if (kind === "datetime") return "date";
74
93
  return "text";
75
94
  }
76
95
  function pageSortFor(fields, sort) {
77
96
  if (!sort) return [];
78
- const field = fields.find((f) => f.key === sort.id);
79
- if (!field) return [];
80
- return [{ field: field.key, direction: sort.direction === "desc" ? "desc" : "asc", as: sortAsFor(field) }];
97
+ const field2 = fields.find((f) => f.key === sort.id);
98
+ if (!field2) return [];
99
+ return [{ field: field2.key, direction: sort.direction === "desc" ? "desc" : "asc", as: sortAsFor(field2) }];
100
+ }
101
+ function containsPattern(text) {
102
+ return `(?i)${text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`;
103
+ }
104
+ var BLANK_VALUE = "__empty__";
105
+ var field = (f) => ({ field: f.id });
106
+ var constant = (value) => ({ const: value });
107
+ var withinWindow = (f, window) => ({ op: "within", args: [field(f), constant(window)] });
108
+ var op = (name, ...args) => ({ op: name, args });
109
+ function clauseFor(f, value, now = /* @__PURE__ */ new Date()) {
110
+ const name = fieldName(f);
111
+ const refused = filterRefusal(f);
112
+ if (refused) return refused;
113
+ const not = (rule) => op("not", rule);
114
+ switch (value.kind) {
115
+ case "text": {
116
+ const mode = value.mode ?? "contains";
117
+ if (mode === "empty" || mode === "null") return value.negated ? { rule: op("present", field(f)) } : { rule: not(op("present", field(f))) };
118
+ if (mode === "not_empty" || mode === "not_null") return value.negated ? { rule: not(op("present", field(f))) } : { rule: op("present", field(f)) };
119
+ const text = value.value.trim();
120
+ if (text === "") return null;
121
+ const kind = String(editorKindFor(f));
122
+ if (kind === "number" || kind === "currency" || kind === "percent" || kind === "checkbox") {
123
+ return `${name} holds numbers, so it is filtered by a range, not by words.`;
124
+ }
125
+ const matches = op("matches", field(f), constant(containsPattern(text)));
126
+ return { rule: value.negated ? not(matches) : matches };
127
+ }
128
+ case "select": {
129
+ const ticked = value.values ?? (value.value === "" ? [] : [value.value]);
130
+ const blank = ticked.includes(BLANK_VALUE);
131
+ const words = ticked.filter((w) => w !== BLANK_VALUE);
132
+ if (!blank && words.length === 0) return value.negated ? null : { rule: constant(false) };
133
+ const present = op("present", field(f));
134
+ const kind = String(editorKindFor(f));
135
+ if (kind === "datetime" || kind === "date") {
136
+ if (words.length === 0) return { rule: value.negated ? present : not(present) };
137
+ const holdsDays = String((f.config ?? {})["kind"] ?? "") === "date";
138
+ if (words.length === 1 && !blank && !value.negated) return { flat: { [f.key]: dayWindow(words[0], holdsDays) } };
139
+ const leaves = words.map((day) => withinWindow(f, dayWindow(day, holdsDays)));
140
+ const anyDay = leaves.length === 1 ? leaves[0] : op("or", ...leaves);
141
+ if (!value.negated) return { rule: blank ? op("or", not(present), anyDay) : anyDay };
142
+ return { rule: blank ? op("and", present, not(anyDay)) : not(anyDay) };
143
+ }
144
+ const asStored = (w) => holdsNumbers(f) || kind === "rating" ? Number.isFinite(Number(w)) ? Number(w) : w : kind === "checkbox" && (w === "true" || w === "false") ? w === "true" : w;
145
+ const stored = words.map(asStored);
146
+ const anyWord = stored.length === 0 ? null : stored.length === 1 ? op("eq", field(f), constant(stored[0])) : op("or", ...stored.map((w) => op("eq", field(f), constant(w))));
147
+ if (!value.negated) {
148
+ if (!blank && stored.length === 1) return { flat: { [f.key]: stored[0] } };
149
+ if (!blank) return { rule: anyWord };
150
+ return { rule: anyWord ? op("or", not(present), anyWord) : not(present) };
151
+ }
152
+ if (blank) return { rule: anyWord ? op("and", present, not(anyWord)) : present };
153
+ return { rule: op("or", not(present), not(anyWord)) };
154
+ }
155
+ case "boolean":
156
+ return value.negated ? { rule: op("ne", field(f), constant(value.value)) } : { flat: { [f.key]: value.value } };
157
+ case "number": {
158
+ const turned = (rule) => ({ rule: value.negated ? not(rule) : rule });
159
+ if (value.op === "empty") return turned(not(op("present", field(f))));
160
+ if (value.op === "not_empty") return turned(op("present", field(f)));
161
+ if (value.op === "eq" || value.op === "ne") {
162
+ if (typeof value.min !== "number") return null;
163
+ return turned(op(value.op, field(f), constant(value.min)));
164
+ }
165
+ if (value.op === "lt") return typeof value.max === "number" ? turned(op("lt", field(f), constant(value.max))) : null;
166
+ if (value.op === "gt") return typeof value.min === "number" ? turned(op("gt", field(f), constant(value.min))) : null;
167
+ const parts = [];
168
+ if (typeof value.min === "number") parts.push(op("gte", field(f), constant(value.min)));
169
+ if (typeof value.max === "number") parts.push(op("lte", field(f), constant(value.max)));
170
+ if (parts.length === 0) return null;
171
+ const range = parts.length === 1 ? parts[0] : op("and", ...parts);
172
+ return { rule: value.negated ? not(range) : range };
173
+ }
174
+ case "date": {
175
+ const zone2 = void 0;
176
+ const moments = dataTable.dateFilterBounds(value.preset ? { ...value, anchor: now.toISOString() } : value, now, zone2);
177
+ const holdsDays = String((f.config ?? {})["kind"] ?? "") === "date";
178
+ const bounds = holdsDays ? dataTable.dateFilterDayBounds(moments, zone2) : moments;
179
+ if (bounds.since === void 0 && bounds.until === void 0) return null;
180
+ return {
181
+ flat: {
182
+ [f.key]: {
183
+ ...bounds.since === void 0 ? {} : { from: bounds.since },
184
+ ...bounds.until === void 0 ? {} : { to: bounds.until }
185
+ }
186
+ }
187
+ };
188
+ }
189
+ default:
190
+ return `${name} cannot be filtered that way by the store.`;
191
+ }
192
+ }
193
+ function dayWindow(day, holdsDays) {
194
+ if (!holdsDays) return dataTable.dayInstantWindow(day, void 0);
195
+ const [y, m, d] = day.split("-").map(Number);
196
+ const ymd = (t) => `${t.getUTCFullYear()}-${String(t.getUTCMonth() + 1).padStart(2, "0")}-${String(t.getUTCDate()).padStart(2, "0")}`;
197
+ return { from: ymd(new Date(Date.UTC(y, (m ?? 1) - 1, d ?? 1))), to: ymd(new Date(Date.UTC(y, (m ?? 1) - 1, (d ?? 1) + 1))) };
198
+ }
199
+ function storeQuestion(fields, base, columnFilters, now = /* @__PURE__ */ new Date()) {
200
+ const refused = [];
201
+ const flat = {};
202
+ const rules = [];
203
+ if (base && records.isRuleFilter(base)) rules.push(base);
204
+ else if (base) Object.assign(flat, base);
205
+ for (const [key, value] of Object.entries(columnFilters)) {
206
+ if (!value) continue;
207
+ const f = fields.find((candidate) => candidate.key === key);
208
+ if (!f) continue;
209
+ const clause = clauseFor(f, value, now);
210
+ if (clause === null) continue;
211
+ if (typeof clause === "string") {
212
+ refused.push(clause);
213
+ continue;
214
+ }
215
+ if ("flat" in clause) Object.assign(flat, clause.flat);
216
+ else rules.push(clause.rule);
217
+ }
218
+ const flatKeys = Object.keys(flat);
219
+ if (rules.length === 0) return { filter: flatKeys.length > 0 ? flat : void 0, refused };
220
+ for (const key of flatKeys) {
221
+ const f = fields.find((candidate) => candidate.key === key);
222
+ const value = flat[key];
223
+ if (!f) continue;
224
+ if (value !== null && typeof value === "object") {
225
+ rules.push(withinWindow(f, value));
226
+ continue;
227
+ }
228
+ rules.push(value === null ? op("not", op("present", field(f))) : op("eq", field(f), constant(value)));
229
+ }
230
+ return { filter: rules.length === 1 ? rules[0] : op("and", ...rules), refused };
231
+ }
232
+
233
+ // src/rowVersion.ts
234
+ function ownVersion(row) {
235
+ const v = row.version;
236
+ return typeof v === "number" ? v : null;
81
237
  }
82
238
 
83
239
  // src/groupReads.ts
@@ -86,18 +242,24 @@ function isPlainGrouping(grouping) {
86
242
  }
87
243
  function groupedPageSort(fields, groupKey, sort) {
88
244
  if (!groupKey) return sort;
89
- const field = fields.find((f) => f.key === groupKey);
90
- if (!field) return sort;
91
- return [{ field: groupKey, direction: "asc", as: sortAsFor(field) }, ...sort.filter((s) => s.field !== groupKey)];
245
+ const field2 = fields.find((f) => f.key === groupKey);
246
+ if (!field2) return sort;
247
+ return [{ field: groupKey, direction: "asc", as: sortAsFor(field2) }, ...sort.filter((s) => s.field !== groupKey)];
92
248
  }
93
249
  function chosenEntries(chosen) {
94
- return JSON.parse(JSON.stringify(Object.entries(chosen).sort())).filter(([, op]) => op in MEASURE_LABEL);
250
+ return JSON.parse(JSON.stringify(Object.entries(chosen).sort())).filter(([, op2]) => op2 in MEASURE_LABEL);
95
251
  }
96
252
  function groupFactMeasures(chosen) {
97
- return [{ op: "count" }, ...chosenEntries(chosen).filter(([, op]) => op !== "count").map(([key, op]) => ({ op, key }))];
253
+ return [{ op: "count" }, ...chosenEntries(chosen).filter(([, op2]) => op2 !== "count").map(([key, op2]) => ({ op: op2, key }))];
98
254
  }
99
255
  function summaryBarMeasures(chosen) {
100
- return chosenEntries(chosen).map(([key, op]) => op === "count" ? { op: "count" } : { op, key });
256
+ return chosenEntries(chosen).map(([key, op2]) => op2 === "count" ? { op: "count" } : { op: op2, key });
257
+ }
258
+ function choiceFieldKeys(fields) {
259
+ return fields.filter((f) => {
260
+ const kind = String(editorKindFor(f));
261
+ return kind === "select" || kind === "multi_select";
262
+ }).map((f) => f.key);
101
263
  }
102
264
  var MIN_ROW_HEIGHT = 24;
103
265
  var MAX_ROW_HEIGHT = 96;
@@ -197,8 +359,8 @@ function parseGridPresentation(raw) {
197
359
  if (footer === "sticky" || footer === "inline") out.footer = footer;
198
360
  if (isRecord(value["summaries"])) {
199
361
  const summaries = {};
200
- for (const [key, op] of Object.entries(value["summaries"])) {
201
- if (typeof op === "string" && op.trim() !== "") summaries[key] = op;
362
+ for (const [key, op2] of Object.entries(value["summaries"])) {
363
+ if (typeof op2 === "string" && op2.trim() !== "") summaries[key] = op2;
202
364
  }
203
365
  if (Object.keys(summaries).length > 0) out.summaries = summaries;
204
366
  }
@@ -206,9 +368,9 @@ function parseGridPresentation(raw) {
206
368
  }
207
369
  function parseGrouping(raw) {
208
370
  if (!isRecord(raw)) return void 0;
209
- const field = raw["field"];
210
- if (typeof field !== "string" || field.trim() === "") return void 0;
211
- const out = { field };
371
+ const field2 = raw["field"];
372
+ if (typeof field2 !== "string" || field2.trim() === "") return void 0;
373
+ const out = { field: field2 };
212
374
  if (isRecord(raw["aggregates"])) {
213
375
  const aggregates = {};
214
376
  for (const [key, spec] of Object.entries(raw["aggregates"])) {
@@ -988,13 +1150,17 @@ async function askTileSeed(input) {
988
1150
  // src/firstPage.ts
989
1151
  var TABLE_PAGE_SIZE = 100;
990
1152
  var EMBEDDED_PAGE_SIZE = 50;
1153
+ function viewFilterOf(shown, fields) {
1154
+ if (!shown) return void 0;
1155
+ const base = shown.where ?? (shown.filters && Object.keys(shown.filters).length > 0 ? shown.filters : void 0);
1156
+ return storeQuestion(fields, base, {}).filter;
1157
+ }
991
1158
  function firstGridPageArgs(input) {
992
1159
  const { shown, fields } = input;
993
1160
  if (!shown) return null;
994
1161
  if ((shown.layout ?? "grid") !== "grid") return null;
995
1162
  if (shown.ruleId) return null;
996
1163
  if (shown.order === "manual") return null;
997
- if (shown.filters && Object.keys(shown.filters).length > 0) return null;
998
1164
  const grouping = shown.presentation?.grouping;
999
1165
  if (grouping && !(input.merged && isPlainGrouping(grouping))) return null;
1000
1166
  const first = (shown.sorts ?? [])[0];
@@ -1002,7 +1168,7 @@ function firstGridPageArgs(input) {
1002
1168
  const opening = viewSort ?? defaultSortOf(input.table, fields);
1003
1169
  return {
1004
1170
  p_table_id: input.tableId,
1005
- p_filter: {},
1171
+ p_filter: viewFilterOf(shown, fields) ?? {},
1006
1172
  p_search: null,
1007
1173
  p_sort: groupedPageSort(fields, grouping?.field ?? null, pageSortFor(fields, opening)),
1008
1174
  p_view_id: null,
@@ -1013,17 +1179,30 @@ function firstGridPageArgs(input) {
1013
1179
  async function firstGridPageRequest(client, tableId, options = {}) {
1014
1180
  return (await firstGridOpening(client, tableId, options))?.args ?? null;
1015
1181
  }
1182
+ function defaultSpec(tableId) {
1183
+ return { name: "All", subject: tableId, layout: "grid", isDefault: true };
1184
+ }
1185
+ function shownFromSpec(spec, tableId) {
1186
+ return { ...spec, subject: spec.subject ?? tableId, id: "", movedFrom: null };
1187
+ }
1016
1188
  async function firstGridOpening(client, tableId, options) {
1189
+ const explicit = options.view;
1017
1190
  const [table, fields, views, looks] = await Promise.all([
1018
1191
  client.tableRead({ table_id: tableId }),
1019
1192
  client.fields({ table_id: tableId }),
1020
- client.views({ table_id: tableId }),
1021
- readLooks(client, tableId)
1193
+ explicit ? Promise.resolve(null) : client.views({ table_id: tableId }),
1194
+ explicit ? Promise.resolve(null) : readLooks(client, tableId)
1022
1195
  ]);
1023
- if (!table.ok || !fields.ok || !views.ok || !looks.ok) return null;
1024
- const mine = views.data.map(viewFromRow).filter((v) => v.subject === tableId);
1025
- const view = defaultViewOf(mine) ?? mine[0] ?? null;
1026
- const shown = view ? applyLook(view, looks.data[view.id]) : null;
1196
+ if (!table.ok || !fields.ok) return null;
1197
+ let shown;
1198
+ if (explicit) {
1199
+ shown = shownFromSpec(explicit, tableId);
1200
+ } else {
1201
+ if (!views?.ok || !looks?.ok) return null;
1202
+ const mine = views.data.map(viewFromRow).filter((v) => v.subject === tableId);
1203
+ const view = defaultViewOf(mine) ?? mine[0] ?? null;
1204
+ shown = view ? applyLook(view, looks.data[view.id]) : shownFromSpec(defaultSpec(tableId), tableId);
1205
+ }
1027
1206
  const args = firstGridPageArgs({
1028
1207
  tableId,
1029
1208
  table: table.data,
@@ -1054,7 +1233,7 @@ async function askTablePageSeed(input) {
1054
1233
  ).catch(() => null) : null;
1055
1234
  try {
1056
1235
  const pageSize = input.pageSize ?? (input.embedded ? EMBEDDED_PAGE_SIZE : TABLE_PAGE_SIZE);
1057
- const opening = await firstGridOpening(client, input.tableId, { pageSize, ...input.merged ? { merged: true } : {} });
1236
+ const opening = await firstGridOpening(client, input.tableId, { pageSize, ...input.merged ? { merged: true } : {}, ...input.view ? { view: input.view } : {} });
1058
1237
  const wantRows = typeof input.rows === "function" ? input.rows(recording.seed()) : input.rows !== false;
1059
1238
  if (opening && wantRows) {
1060
1239
  const { args, shown, fields } = opening;
@@ -1071,20 +1250,28 @@ async function askTablePageSeed(input) {
1071
1250
  const chosen = shown?.presentation?.summaries ?? {};
1072
1251
  const groupKey = shown?.presentation?.grouping?.field ?? null;
1073
1252
  const reads = [];
1253
+ const filter = args.p_filter && Object.keys(args.p_filter).length > 0 ? args.p_filter : void 0;
1074
1254
  if (groupKey) {
1075
- reads.push(client.recordAggregate({ table_id: input.tableId, groupBy: [groupKey], measures: groupFactMeasures(chosen), limit: 1e3 }));
1255
+ reads.push(client.recordAggregate({ table_id: input.tableId, groupBy: [groupKey], measures: groupFactMeasures(chosen), limit: 1e3, ...filter ? { filter } : {} }));
1076
1256
  }
1077
1257
  const bar = summaryBarMeasures(chosen);
1078
- if (bar.length > 0) reads.push(client.recordAggregate({ table_id: input.tableId, measures: bar }));
1079
- for (const field of fields) {
1080
- if (field.type !== "list" && field.type !== "relation") continue;
1258
+ if (bar.length > 0) reads.push(client.recordAggregate({ table_id: input.tableId, measures: bar, ...filter ? { filter } : {} }));
1259
+ for (const field2 of fields) {
1260
+ if (field2.type !== "list" && field2.type !== "relation") continue;
1081
1261
  const ids = /* @__PURE__ */ new Set();
1082
1262
  for (const row of page.data.rows) {
1083
- const value = row.document?.[field.key];
1263
+ const value = row.document?.[field2.key];
1084
1264
  for (const id of Array.isArray(value) ? value.map(String) : typeof value === "string" ? [value] : []) if (uuid.isUuidShape(id)) ids.add(id);
1085
1265
  }
1086
- if (ids.size > 0) reads.push(client.relationWordsMany({ field_id: field.id, record_ids: [...ids] }));
1266
+ if (ids.size > 0) reads.push(client.relationWordsMany({ field_id: field2.id, record_ids: [...ids] }));
1267
+ }
1268
+ if (input.merged) {
1269
+ for (const key of choiceFieldKeys(fields)) {
1270
+ reads.push(client.recordAggregate({ table_id: input.tableId, groupBy: [key], measures: [{ op: "count" }], limit: 500, ...filter ? { filter } : {} }));
1271
+ }
1087
1272
  }
1273
+ const unversioned = page.data.rows.filter((row) => ownVersion(row) === null).map((row) => row.id);
1274
+ if (unversioned.length > 0) reads.push(client.recordHeaders({ ids: unversioned }));
1088
1275
  await Promise.all(reads.map((read) => read.catch(() => null)));
1089
1276
  }
1090
1277
  }
@@ -1104,5 +1291,6 @@ exports.firstGridPageArgs = firstGridPageArgs;
1104
1291
  exports.firstGridPageRequest = firstGridPageRequest;
1105
1292
  exports.mergeSeeds = mergeSeeds;
1106
1293
  exports.serverRowsOf = serverRowsOf;
1294
+ exports.viewFilterOf = viewFilterOf;
1107
1295
  //# sourceMappingURL=first-page.cjs.map
1108
1296
  //# sourceMappingURL=first-page.cjs.map