@vuu-ui/vuu-utils 4.0.0-alpha.9 → 4.0.0-beta.1

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 (133) hide show
  1. package/package.json +10 -9
  2. package/src/array-utils.js +5 -4
  3. package/src/column-utils.js +33 -20
  4. package/src/component-registry.js +3 -2
  5. package/src/data-utils.js +1 -0
  6. package/src/datasource/BaseDataSource.js +5 -3
  7. package/src/date/EpochTimestamp.js +85 -0
  8. package/src/date/date-utils.js +50 -18
  9. package/src/date/dateTimePattern.js +33 -5
  10. package/src/date/formatter.js +54 -16
  11. package/src/date/index.js +5 -2
  12. package/src/date/temporal.js +131 -0
  13. package/src/date/time-zone.js +56 -0
  14. package/src/event-emitter.js +1 -3
  15. package/src/feature-utils.js +14 -8
  16. package/src/filters/filter-utils.js +13 -7
  17. package/src/filters/filterAsQuery.js +107 -45
  18. package/src/filters/index.js +1 -0
  19. package/src/filters/temporal-cell-filter.js +63 -0
  20. package/src/form-utils.js +44 -6
  21. package/src/formatting-utils.js +46 -25
  22. package/src/html-utils.js +35 -1
  23. package/src/index.js +0 -1
  24. package/src/keyset.js +15 -9
  25. package/src/react-utils.js +8 -2
  26. package/src/table-schema-utils.js +6 -5
  27. package/src/useResizeObserver.js +3 -3
  28. package/src/vendor/@preact/signals-core/dist/signals-core.js +1 -1
  29. package/src/vendor/@preact/signals-core/dist/signals-core.js.map +1 -1
  30. package/types/ThemeProvider.d.ts +1 -1
  31. package/types/array-utils.d.ts +3 -2
  32. package/types/column-utils.d.ts +45 -43
  33. package/types/context-definitions/DataProvider.d.ts +1 -1
  34. package/types/context-definitions/DataSourceProvider.d.ts +1 -1
  35. package/types/datasource/BaseDataSource.d.ts +2 -2
  36. package/types/date/EpochTimestamp.d.ts +66 -0
  37. package/types/date/date-utils.d.ts +34 -9
  38. package/types/date/dateTimePattern.d.ts +20 -2
  39. package/types/date/formatter.d.ts +26 -1
  40. package/types/date/index.d.ts +6 -2
  41. package/types/date/temporal.d.ts +68 -0
  42. package/types/date/time-zone.d.ts +52 -0
  43. package/types/feature-utils.d.ts +34 -30
  44. package/types/filters/filter-utils.d.ts +6 -1
  45. package/types/filters/filterAsQuery.d.ts +41 -0
  46. package/types/filters/index.d.ts +1 -0
  47. package/types/filters/temporal-cell-filter.d.ts +27 -0
  48. package/types/form-utils.d.ts +19 -0
  49. package/types/formatting-utils.d.ts +23 -1
  50. package/types/html-utils.d.ts +16 -0
  51. package/types/index.d.ts +0 -1
  52. package/types/keyset.d.ts +11 -1
  53. package/types/react-utils.d.ts +1 -1
  54. package/types/sort-utils.d.ts +2 -2
  55. package/types/table-schema-utils.d.ts +2 -2
  56. package/types/tree-types.d.ts +2 -0
  57. package/types/src/Clock.d.ts +0 -20
  58. package/types/src/PageVisibilityObserver.d.ts +0 -36
  59. package/types/src/ScaledDecimal.d.ts +0 -12
  60. package/types/src/ShellContext.d.ts +0 -11
  61. package/types/src/ThemeProvider.d.ts +0 -32
  62. package/types/src/array-utils.d.ts +0 -16
  63. package/types/src/box-utils.d.ts +0 -24
  64. package/types/src/broadcast-channel.d.ts +0 -16
  65. package/types/src/column-utils.d.ts +0 -243
  66. package/types/src/common-types.d.ts +0 -6
  67. package/types/src/component-registry.d.ts +0 -65
  68. package/types/src/context-definitions/DataContext.d.ts +0 -21
  69. package/types/src/context-definitions/DataProvider.d.ts +0 -7
  70. package/types/src/context-definitions/DataSourceProvider.d.ts +0 -12
  71. package/types/src/context-definitions/WorkspaceContext.d.ts +0 -17
  72. package/types/src/cookie-utils.d.ts +0 -9
  73. package/types/src/css-utils.d.ts +0 -1
  74. package/types/src/data-utils.d.ts +0 -33
  75. package/types/src/datasource/BaseDataSource.d.ts +0 -63
  76. package/types/src/datasource/datasource-action-utils.d.ts +0 -7
  77. package/types/src/datasource/datasource-filter-utils.d.ts +0 -2
  78. package/types/src/datasource/datasource-utils.d.ts +0 -61
  79. package/types/src/date/date-utils.d.ts +0 -39
  80. package/types/src/date/dateTimePattern.d.ts +0 -8
  81. package/types/src/date/formatter.d.ts +0 -9
  82. package/types/src/date/index.d.ts +0 -4
  83. package/types/src/date/types.d.ts +0 -29
  84. package/types/src/debug-utils.d.ts +0 -9
  85. package/types/src/edit-utils.d.ts +0 -2
  86. package/types/src/errors.d.ts +0 -2
  87. package/types/src/event-emitter.d.ts +0 -21
  88. package/types/src/feature-utils.d.ts +0 -110
  89. package/types/src/filters/filter-utils.d.ts +0 -75
  90. package/types/src/filters/filterAsQuery.d.ts +0 -8
  91. package/types/src/filters/index.d.ts +0 -2
  92. package/types/src/form-utils.d.ts +0 -37
  93. package/types/src/formatting-utils.d.ts +0 -14
  94. package/types/src/getUniqueId.d.ts +0 -1
  95. package/types/src/group-utils.d.ts +0 -10
  96. package/types/src/html-utils.d.ts +0 -21
  97. package/types/src/index.d.ts +0 -76
  98. package/types/src/input-utils.d.ts +0 -2
  99. package/types/src/invariant.d.ts +0 -1
  100. package/types/src/itemToString.d.ts +0 -2
  101. package/types/src/json-types.d.ts +0 -52
  102. package/types/src/json-utils.d.ts +0 -6
  103. package/types/src/keyboard-utils.d.ts +0 -15
  104. package/types/src/keyset.d.ts +0 -16
  105. package/types/src/layout-types.d.ts +0 -22
  106. package/types/src/list-utils.d.ts +0 -2
  107. package/types/src/local-storage-utils.d.ts +0 -4
  108. package/types/src/logging-utils.d.ts +0 -45
  109. package/types/src/module-utils.d.ts +0 -8
  110. package/types/src/nanoid/index.d.ts +0 -1
  111. package/types/src/perf-utils.d.ts +0 -5
  112. package/types/src/promise-utils.d.ts +0 -8
  113. package/types/src/protocol-message-utils.d.ts +0 -43
  114. package/types/src/range-utils.d.ts +0 -29
  115. package/types/src/react-utils.d.ts +0 -15
  116. package/types/src/round-decimal.d.ts +0 -4
  117. package/types/src/row-utils.d.ts +0 -28
  118. package/types/src/selection-utils.d.ts +0 -9
  119. package/types/src/shell-layout-types.d.ts +0 -15
  120. package/types/src/sort-utils.d.ts +0 -11
  121. package/types/src/table-schema-utils.d.ts +0 -5
  122. package/types/src/text-utils.d.ts +0 -10
  123. package/types/src/theme-utils.d.ts +0 -14
  124. package/types/src/tree-types.d.ts +0 -9
  125. package/types/src/tree-utils.d.ts +0 -9
  126. package/types/src/ts-utils.d.ts +0 -20
  127. package/types/src/typeahead-utils.d.ts +0 -1
  128. package/types/src/url-utils.d.ts +0 -3
  129. package/types/src/useId.d.ts +0 -1
  130. package/types/src/useLayoutEffectSkipFirst.d.ts +0 -2
  131. package/types/src/useResizeObserver.d.ts +0 -15
  132. package/types/src/useStateRef.d.ts +0 -2
  133. package/types/src/user-types.d.ts +0 -3
package/package.json CHANGED
@@ -1,10 +1,9 @@
1
1
  {
2
- "version": "4.0.0-alpha.9",
2
+ "version": "4.0.0-beta.1",
3
3
  "author": "heswell",
4
4
  "main": "./src/index.js",
5
5
  "license": "Apache-2.0",
6
6
  "types": "types/index.d.ts",
7
- "type": "module",
8
7
  "scripts": {
9
8
  "build:dev": "node ../../scripts/run-build.mjs",
10
9
  "build": "node ../../scripts/run-build-rslib.ts",
@@ -12,18 +11,18 @@
12
11
  "type-defs": "node ../../scripts/build-type-defs.mjs"
13
12
  },
14
13
  "dependencies": {
15
- "@salt-ds/core": "1.67.0"
14
+ "@salt-ds/core": "1.72.0"
16
15
  },
17
16
  "devDependencies": {
18
17
  "@dnd-kit/react": "^0.1.21",
19
- "@vuu-ui/vuu-data-types": "4.0.0-alpha.9",
20
- "@vuu-ui/vuu-table-types": "4.0.0-alpha.9",
21
- "@vuu-ui/vuu-filter-types": "4.0.0-alpha.9",
22
- "@vuu-ui/vuu-protocol-types": "4.0.0-alpha.9"
18
+ "@vuu-ui/vuu-data-types": "4.0.0-beta.1",
19
+ "@vuu-ui/vuu-table-types": "4.0.0-beta.1",
20
+ "@vuu-ui/vuu-filter-types": "4.0.0-beta.1",
21
+ "@vuu-ui/vuu-protocol-types": "4.0.0-beta.1"
23
22
  },
24
23
  "peerDependencies": {
25
24
  "@internationalized/date": "^3.0.0",
26
- "@vuu-ui/vuu-filter-parser": "4.0.0-alpha.9",
25
+ "@vuu-ui/vuu-filter-parser": "4.0.0-beta.1",
27
26
  "clsx": "^2.0.0",
28
27
  "react": "^19.2.3",
29
28
  "react-dom": "^19.2.3"
@@ -38,9 +37,11 @@
38
37
  "exports": {
39
38
  ".": {
40
39
  "import": "./src/index.js",
40
+ "default": "./src/index.js",
41
41
  "types": "./types/index.d.ts"
42
42
  }
43
43
  },
44
44
  "module": "./src/index.js",
45
- "name": "@vuu-ui/vuu-utils"
45
+ "name": "@vuu-ui/vuu-utils",
46
+ "type": "module"
46
47
  }
@@ -1,3 +1,4 @@
1
+ const simpleIdentity = (a, b)=>a === b;
1
2
  function partition(array, test, pass = [], fail = []) {
2
3
  for(let i = 0, len = array.length; i < len; i++)(test(array[i], i) ? pass : fail).push(array[i]);
3
4
  return [
@@ -41,15 +42,15 @@ const moveItem = (items, fromIndex, toIndex)=>{
41
42
  }
42
43
  }
43
44
  };
44
- const getAddedItems = (currentItems, newItems)=>{
45
- const isNew = (i)=>!currentItems?.includes(i);
45
+ const getAddedItems = (currentItems, newItems, compareItems = simpleIdentity)=>{
46
46
  if (void 0 === currentItems) return newItems;
47
+ const isNew = (item)=>currentItems.every((currentItem)=>false === compareItems(item, currentItem));
47
48
  if (newItems.some(isNew)) return newItems.filter(isNew);
48
49
  return [];
49
50
  };
50
- const getRemovedItems = (currentItems, newItems)=>{
51
- const isRemoved = (i)=>!newItems?.includes(i);
51
+ const getRemovedItems = (currentItems, newItems, compareItems = simpleIdentity)=>{
52
52
  if (void 0 === newItems) return currentItems;
53
+ const isRemoved = (item)=>newItems.every((newItem)=>false === compareItems(item, newItem));
53
54
  if (currentItems.some(isRemoved)) return currentItems.filter(isRemoved);
54
55
  return [];
55
56
  };
@@ -1,7 +1,8 @@
1
1
  import { moveItem } from "./array-utils.js";
2
+ import { getTemporalInfo } from "./date/temporal.js";
2
3
  import { queryClosest } from "./html-utils.js";
3
- const NO_HEADINGS = [];
4
4
  const SORT_ASC = "asc";
5
+ const NO_HEADINGS = [];
5
6
  const DEFAULT_COL_WIDTH = 100;
6
7
  const DEFAULT_MAX_WIDTH = 250;
7
8
  const DEFAULT_MIN_WIDTH = 50;
@@ -44,14 +45,17 @@ const getRuntimeColumnWidth = (col, runtimeColumns)=>{
44
45
  if (runtimeColumn) return runtimeColumn.width;
45
46
  return DEFAULT_COL_WIDTH;
46
47
  };
47
- const applyRuntimeColumnWidthsToConfig = (tableConfig, columns)=>({
48
+ const applyRuntimeColumnWidthsToConfig = (tableConfig, columns)=>{
49
+ const runtimeColumns = columns.flatMap((column)=>isGroupColumn(column) ? column.columns : column);
50
+ return {
48
51
  ...tableConfig,
49
- columns: columns.map((column)=>({
52
+ columns: tableConfig.columns.map((column)=>({
50
53
  ...column,
51
- width: column.width ?? getRuntimeColumnWidth(column, columns)
54
+ width: runtimeColumns.find(({ name })=>name === column.name)?.width ?? column.width ?? DEFAULT_COL_WIDTH
52
55
  })),
53
56
  columnLayout: "manual"
54
- });
57
+ };
58
+ };
55
59
  const isValidColumnAlignment = (v)=>"left" === v || "right" === v;
56
60
  const isValidPinLocation = (v)=>isValidColumnAlignment(v) || "floating" === v || "" === v;
57
61
  const VUU_COLUMN_DATA_TYPES = [
@@ -77,22 +81,35 @@ const fromServerDataType = (serverDataType)=>{
77
81
  return "number";
78
82
  case "boolean":
79
83
  return "boolean";
84
+ case "epochtimestamp":
85
+ case "epochtimestampnano":
86
+ return "date/time";
80
87
  default:
81
88
  return "string";
82
89
  }
83
90
  };
84
91
  const isNanoTimestampColumn = ({ serverDataType })=>"epochtimestampnano" === serverDataType;
85
92
  const isTimestampColumn = (column)=>"epochtimestamp" === column.serverDataType || isNanoTimestampColumn(column);
86
- const isNumericColumn = ({ serverDataType, type })=>{
93
+ const isTemporalColumn = (column)=>void 0 !== getTemporalInfo(column);
94
+ const isNumericColumn = (column)=>{
95
+ const { serverDataType, type } = column;
96
+ if (isTemporalColumn(column)) return false;
87
97
  if ("int" === serverDataType || "long" === serverDataType || "double" === serverDataType) return true;
88
98
  if ("string" == typeof type) return "number" === type;
89
99
  if ("string" == typeof type?.name) return type?.name === "number";
90
100
  return false;
91
101
  };
92
- const isDateTimeDataType = (column)=>(isTypeDescriptor(column.type) ? column.type.name : column.type) === "date/time";
102
+ const isDateTimeDataType = (column)=>{
103
+ const typeName = isTypeDescriptor(column.type) ? column.type.name : column.type;
104
+ return "date/time" === typeName || "date" === typeName;
105
+ };
93
106
  const isTimeDataType = (column)=>(isTypeDescriptor(column.type) ? column.type.name : column.type) === "time";
94
- const isDateTimeDataValue = (column)=>isTimestampColumn(column) || isDateTimeDataType(column);
95
- const isTimeDataValue = (column)=>(isTypeDescriptor(column?.type) ? column.type.name : column?.type) === "time";
107
+ const isDateTimeDataValue = (column)=>{
108
+ const kind = getTemporalInfo(column)?.kind;
109
+ return "datetime" === kind || "date" === kind;
110
+ };
111
+ const isTimeDataValue = (column)=>getTemporalInfo(column)?.kind === "time";
112
+ const isTimeDataValueWithMilliseconds = (column)=>isTimeDataValue(column) && isTypeDescriptor(column.type) && column.type.formatting?.pattern?.time === "hh:mm:ss.ms";
96
113
  const isPinned = (column)=>"string" == typeof column.pin;
97
114
  const hasHeadings = (column)=>Array.isArray(column.heading) && column.heading.length > 0;
98
115
  const isResizing = (column)=>column.resizing;
@@ -297,7 +314,7 @@ const measurePinnedColumns = (columns, selectionEndSize)=>{
297
314
  unpinnedWidth
298
315
  };
299
316
  };
300
- const getTableHeadings = (columns, selectionBookendWidth = 0)=>{
317
+ const getTableHeadings = (columns)=>{
301
318
  if (columns.some(hasHeadings)) {
302
319
  const maxHeadingDepth = columns.reduce((max, { heading })=>Math.max(max, heading?.length ?? 0), 0);
303
320
  let heading;
@@ -305,9 +322,8 @@ const getTableHeadings = (columns, selectionBookendWidth = 0)=>{
305
322
  let tableHeadingsRow;
306
323
  for(let level = 0; level < maxHeadingDepth; level++){
307
324
  tableHeadingsRow = [];
308
- columns.forEach(({ heading: columnHeading = NO_HEADINGS, hidden, width })=>{
325
+ columns.forEach(({ heading: columnHeading = NO_HEADINGS, width })=>{
309
326
  const label = columnHeading[level] ?? "";
310
- if (hidden) return;
311
327
  if (heading && heading.label === label) heading.width += width;
312
328
  else {
313
329
  heading = {
@@ -319,10 +335,6 @@ const getTableHeadings = (columns, selectionBookendWidth = 0)=>{
319
335
  });
320
336
  tableHeadings.push(tableHeadingsRow);
321
337
  }
322
- if (selectionBookendWidth > 0) tableHeadings.forEach((tableHeading)=>{
323
- tableHeading[0].width += selectionBookendWidth;
324
- tableHeading[tableHeading.length - 1].width += selectionBookendWidth;
325
- });
326
338
  return tableHeadings;
327
339
  }
328
340
  return NO_HEADINGS;
@@ -538,6 +550,7 @@ const getDefaultColumnType = (serverDataType)=>{
538
550
  case "boolean":
539
551
  return "boolean";
540
552
  case "epochtimestamp":
553
+ case "epochtimestampnano":
541
554
  return "date/time";
542
555
  default:
543
556
  return "string";
@@ -596,7 +609,7 @@ const getTypeFormattingFromColumn = (column)=>{
596
609
  };
597
610
  const assertAllColumnsAreIncludedInSubscription = (columns, columnNames)=>{
598
611
  const unsubscribedColumns = [];
599
- for (const column of columns)if ("client" !== column.source && 'vuuAction' !== column.name && !columnNames?.includes(column.name)) unsubscribedColumns.push(column.name);
612
+ for (const column of columns)if ("client" !== column.source && "vuuAction" !== column.name && !columnNames?.includes(column.name)) unsubscribedColumns.push(column.name);
600
613
  if (unsubscribedColumns.length > 0) throw Error(`[column-utils] assertAllColumnsAreIncludedInSubscription columns not included in subscription: ${unsubscribedColumns.join(", ")}`);
601
614
  };
602
615
  const subscribedOnly = (columnNames)=>(column)=>"client" === column.source || columnNames?.includes(column.name);
@@ -631,7 +644,7 @@ const getCalculatedColumnDetails = (column)=>{
631
644
  serverDataType: isVuuColumnDataType(serverDataType) ? serverDataType : void 0
632
645
  };
633
646
  }
634
- throw Error('column.name is not a calculated column');
647
+ throw Error("column.name is nor a calculated column");
635
648
  };
636
649
  const setCalculatedColumnName = (column, name)=>{
637
650
  const [, type, expression] = column.name.split(":");
@@ -778,7 +791,7 @@ const stretchColumnsToFillAvailableSpace = (columns, availableWidth, totalWidth,
778
791
  return adjustedColumns;
779
792
  };
780
793
  const dataAndColumnUnchanged = (p, p1)=>p.column === p1.column && p.column.valueFormatter(p.dataRow[p.column.name]) === p1.column.valueFormatter(p1.dataRow[p1.column.name]);
781
- const dataColumnAndKeyUnchanged = (p, p1)=>p.column === p1.column && p.dataRow.key === p1.dataRow.key && p.dataRow.vuu_action === p1.dataRow.vuu_action && p.column.valueFormatter(p.dataRow[p.column.name]) === p1.column.valueFormatter(p1.dataRow[p1.column.name]);
794
+ const dataColumnAndKeyUnchanged = (p, p1)=>p.column === p1.column && p.dataRow.key === p1.dataRow.key && p.column.valueFormatter(p.dataRow[p.column.name]) === p1.column.valueFormatter(p1.dataRow[p1.column.name]);
782
795
  const toColumnName = (column)=>column.name;
783
796
  const isStringColumn = (column)=>"string" === column.serverDataType;
784
797
  const columnByAriaIndex = (columns, ariaColIndex)=>{
@@ -789,4 +802,4 @@ const columnByAriaIndex = (columns, ariaColIndex)=>{
789
802
  }
790
803
  throw Error(`no column with aria-colIndex ${ariaColIndex}`);
791
804
  };
792
- export { AggregationType, NO_HEADINGS, addColumnToSubscribedColumns, applyDefaultColumnConfig, applyGroupByToColumns, applyRuntimeColumnWidthsToConfig, applySortToColumns, applyWidthToColumns, assertAllColumnsAreIncludedInSubscription, buildColumnMap, checkConfirmationPending, columnByAriaIndex, dataAndColumnUnchanged, dataColumnAndKeyUnchanged, dataSourceRowToDataRowDto, existingSort, extractGroupColumn, findColumn, flattenColumnGroup, fromServerDataType, getAllCellsInColumn, getAriaColIndex, getAriaRowIndex, getCalculatedColumnDetails, getColumnLabel, getColumnName, getColumnStyle, getColumnsInViewport, getDefaultAlignment, getDefaultColumnType, getGroupIcon, getGroupValue, getPinStateFromElement, getRuntimeColumnWidth, getServerDataType, getTableHeadings, getTypeFormattingFromColumn, hasCustomRenderer, hasHeadings, hasValidationRules, isCalculatedColumn, isColumnTypeRenderer, isDataLoading, isDataValueEditable, isDateTimeDataType, isDateTimeDataValue, isGroupColumn, isJsonAttribute, isJsonColumn, isJsonGroup, isLookupRenderer, isMappedValueTypeRenderer, isNanoTimestampColumn, isNotHidden, isNumericColumn, isPinned, isResizing, isStringColumn, isTextColumn, isTimeDataType, isTimeDataValue, isTimestampColumn, isTypeDescriptor, isValidColumnAlignment, isValidPinLocation, isValueListRenderer, isVuuColumnDataType, mapSortCriteria, measurePinnedColumns, metadataKeys, moveColumnTo, projectUpdates, removeSort, replaceColumn, setAggregations, setCalculatedColumnExpression, setCalculatedColumnName, setCalculatedColumnType, sortPinnedColumns, subscribedOnly, toColumnDescriptor, toColumnName, toDataSourceColumns, updateColumn, updateColumnFormatting, updateColumnRenderProps, updateColumnType, visibleColumnAtIndex };
805
+ export { AggregationType, addColumnToSubscribedColumns, applyDefaultColumnConfig, applyGroupByToColumns, applyRuntimeColumnWidthsToConfig, applySortToColumns, applyWidthToColumns, assertAllColumnsAreIncludedInSubscription, buildColumnMap, checkConfirmationPending, columnByAriaIndex, dataAndColumnUnchanged, dataColumnAndKeyUnchanged, dataSourceRowToDataRowDto, existingSort, extractGroupColumn, findColumn, flattenColumnGroup, fromServerDataType, getAllCellsInColumn, getAriaColIndex, getAriaRowIndex, getCalculatedColumnDetails, getColumnLabel, getColumnName, getColumnStyle, getColumnsInViewport, getDefaultAlignment, getDefaultColumnType, getGroupIcon, getGroupValue, getPinStateFromElement, getRuntimeColumnWidth, getServerDataType, getTableHeadings, getTypeFormattingFromColumn, hasCustomRenderer, hasHeadings, hasValidationRules, isCalculatedColumn, isColumnTypeRenderer, isDataLoading, isDataValueEditable, isDateTimeDataType, isDateTimeDataValue, isGroupColumn, isJsonAttribute, isJsonColumn, isJsonGroup, isLookupRenderer, isMappedValueTypeRenderer, isNanoTimestampColumn, isNotHidden, isNumericColumn, isPinned, isResizing, isStringColumn, isTemporalColumn, isTextColumn, isTimeDataType, isTimeDataValue, isTimeDataValueWithMilliseconds, isTimestampColumn, isTypeDescriptor, isValidColumnAlignment, isValidPinLocation, isValueListRenderer, isVuuColumnDataType, mapSortCriteria, measurePinnedColumns, metadataKeys, moveColumnTo, projectUpdates, removeSort, replaceColumn, setAggregations, setCalculatedColumnExpression, setCalculatedColumnName, setCalculatedColumnType, sortPinnedColumns, subscribedOnly, toColumnDescriptor, toColumnName, toDataSourceColumns, updateColumn, updateColumnFormatting, updateColumnRenderProps, updateColumnType, visibleColumnAtIndex };
@@ -1,4 +1,4 @@
1
- import { hasCustomRenderer, isColumnTypeRenderer, isDataValueEditable, isTypeDescriptor } from "./column-utils.js";
1
+ import { hasCustomRenderer, isColumnTypeRenderer, isTemporalColumn, isTypeDescriptor } from "./column-utils.js";
2
2
  const containersSet = new Set();
3
3
  const viewsSet = new Set();
4
4
  const layoutComponentsMap = new Map();
@@ -76,7 +76,8 @@ function getEditRuleValidator(name) {
76
76
  }
77
77
  function dataCellRenderer(column) {
78
78
  if ("boolean" === column.serverDataType && !hasCustomRenderer(column.type)) return cellRenderersMap.get("checkbox-cell");
79
- if (isDataValueEditable(column, "update") && !hasCustomRenderer(column.type)) return cellRenderersMap.get("input-cell");
79
+ if (column.editable && !hasCustomRenderer(column.type) && isTemporalColumn(column) && cellRenderersMap.has("temporal-input-cell")) return cellRenderersMap.get("temporal-input-cell");
80
+ if (column.editable && !hasCustomRenderer(column.type)) return cellRenderersMap.get("input-cell");
80
81
  if (isTypeDescriptor(column.type) && isColumnTypeRenderer(column.type.renderer)) return cellRenderersMap.get(column.type.renderer?.name);
81
82
  if ("boolean" === column.serverDataType) return cellRenderersMap.get("checkbox-cell");
82
83
  }
package/src/data-utils.js CHANGED
@@ -56,6 +56,7 @@ const dataDescriptorTypeToVuuRowDataItemType = ({ serverDataType = "string" })=>
56
56
  case "double":
57
57
  case "int":
58
58
  case "long":
59
+ case "epochtimestamp":
59
60
  return "number";
60
61
  case "boolean":
61
62
  return "boolean";
@@ -3,7 +3,7 @@ import { EventEmitter } from "../event-emitter.js";
3
3
  import { uuid } from "../nanoid/index.js";
4
4
  import { isConfigChanged, stripVisualLink, vanillaConfig, withConfigDefaults } from "./datasource-utils.js";
5
5
  import { Range } from "../range-utils.js";
6
- import { filterAsQuery } from "../filters/index.js";
6
+ import { filterAsQuery, getColumnsByNameForFilter } from "../filters/index.js";
7
7
  const defaultSuspenseProps = {
8
8
  escalateToDisable: true
9
9
  };
@@ -88,9 +88,11 @@ class BaseDataSource extends EventEmitter {
88
88
  filterSpec: filter
89
89
  };
90
90
  }
91
- setFilter(filter) {
91
+ setFilter(filter, options) {
92
92
  const dataSourceFilter = {
93
- filter: filterAsQuery(filter),
93
+ filter: filterAsQuery(filter, {
94
+ columnsByName: getColumnsByNameForFilter(this.tableSchema, options?.columnsByName)
95
+ }),
94
96
  filterStruct: filter
95
97
  };
96
98
  this.filter = dataSourceFilter;
@@ -0,0 +1,85 @@
1
+ import { fromDateTimeFields, getDateTimeFields } from "./time-zone.js";
2
+ const NANOS_PER_MILLI = 1000000n;
3
+ const integerPattern = /^-?\d+$/;
4
+ const MAX_EPOCH_MILLIS = 8.64e15;
5
+ class EpochTimestamp {
6
+ epochMillis;
7
+ subMilliNanos;
8
+ constructor(epochMillis, subMilliNanos = 0){
9
+ this.epochMillis = epochMillis;
10
+ this.subMilliNanos = subMilliNanos;
11
+ }
12
+ static fromMillis(epochMillis, subMilliNanos = 0) {
13
+ const millis = Math.floor(epochMillis);
14
+ return new EpochTimestamp(millis, subMilliNanos);
15
+ }
16
+ static fromNanos(epochNanos) {
17
+ const nanos = "bigint" == typeof epochNanos ? epochNanos : BigInt(epochNanos);
18
+ let millis = nanos / NANOS_PER_MILLI;
19
+ let remainder = nanos % NANOS_PER_MILLI;
20
+ if (remainder < 0n) {
21
+ millis -= 1n;
22
+ remainder += NANOS_PER_MILLI;
23
+ }
24
+ return new EpochTimestamp(Number(millis), Number(remainder));
25
+ }
26
+ static fromDate(date) {
27
+ return new EpochTimestamp(date.getTime());
28
+ }
29
+ static fromFields(fields, timeZone, subMilliNanos = 0) {
30
+ return new EpochTimestamp(fromDateTimeFields(fields, timeZone), subMilliNanos);
31
+ }
32
+ static isEmpty(value) {
33
+ return null == value || "" === value || 0 === value || "0" === value || 0n === value;
34
+ }
35
+ static fromWire(value, encoding) {
36
+ const ts = EpochTimestamp.decode(value, encoding);
37
+ return void 0 !== ts && Math.abs(ts.epochMillis) <= MAX_EPOCH_MILLIS ? ts : void 0;
38
+ }
39
+ static decode(value, encoding) {
40
+ if (EpochTimestamp.isEmpty(value)) return;
41
+ if (value instanceof EpochTimestamp) return value;
42
+ if ("epochNanos" === encoding) {
43
+ if ("bigint" == typeof value) return EpochTimestamp.fromNanos(value);
44
+ else if ("string" == typeof value && integerPattern.test(value)) return EpochTimestamp.fromNanos(value);
45
+ else if ("number" == typeof value && Number.isFinite(value)) return EpochTimestamp.fromNanos(BigInt(Math.trunc(value)));
46
+ } else if ("number" == typeof value && Number.isFinite(value)) return EpochTimestamp.fromMillis(value);
47
+ else if ("bigint" == typeof value) return EpochTimestamp.fromMillis(Number(value));
48
+ else if ("string" == typeof value && integerPattern.test(value)) return EpochTimestamp.fromMillis(Number(value));
49
+ }
50
+ get epochNanos() {
51
+ return BigInt(this.epochMillis) * NANOS_PER_MILLI + BigInt(this.subMilliNanos);
52
+ }
53
+ toDate() {
54
+ return new Date(this.epochMillis);
55
+ }
56
+ fractionalSecond(digits) {
57
+ if (digits <= 0) return "";
58
+ const millisOfSecond = (this.epochMillis % 1000 + 1000) % 1000;
59
+ const nineDigits = `${millisOfSecond}`.padStart(3, "0").concat(`${this.subMilliNanos}`.padStart(6, "0"));
60
+ return nineDigits.slice(0, Math.min(digits, 9));
61
+ }
62
+ getFields(timeZone) {
63
+ return getDateTimeFields(this.epochMillis, timeZone);
64
+ }
65
+ toWire(encoding) {
66
+ return "epochNanos" === encoding ? this.epochNanos.toString() : this.epochMillis;
67
+ }
68
+ toLiteral(encoding) {
69
+ return `${this.toWire(encoding)}`;
70
+ }
71
+ addUnits(units, encoding) {
72
+ return "epochNanos" === encoding ? EpochTimestamp.fromNanos(this.epochNanos + BigInt(units)) : EpochTimestamp.fromMillis(this.epochMillis + units, this.subMilliNanos);
73
+ }
74
+ compare(other) {
75
+ return this.epochMillis === other.epochMillis ? this.subMilliNanos - other.subMilliNanos : this.epochMillis - other.epochMillis;
76
+ }
77
+ equals(other) {
78
+ return void 0 !== other && 0 === this.compare(other);
79
+ }
80
+ toString() {
81
+ const fraction = this.subMilliNanos ? this.fractionalSecond(9) : this.fractionalSecond(3);
82
+ return this.toDate().toISOString().replace(/\.\d{3}Z$/, `.${fraction}Z`);
83
+ }
84
+ }
85
+ export { EpochTimestamp };
@@ -3,50 +3,71 @@ function toCalendarDate(d) {
3
3
  return new CalendarDate(d.getFullYear(), d.getMonth() + 1, d.getDate());
4
4
  }
5
5
  const zeroTime = "00:00:00";
6
+ const zeroTimeMillis = "00:00:00.000";
6
7
  const zeroTimeUnit = "00";
8
+ const unitMaxValue = {
9
+ hours: 23,
10
+ minutes: 59,
11
+ seconds: 59,
12
+ milliseconds: 999
13
+ };
14
+ const unitLength = (unit)=>"milliseconds" === unit ? 3 : 2;
7
15
  function incrementTimeUnitValue(unit, value) {
8
16
  const num = parseInt(value);
9
- if ("hours" === unit && num < 23) return `${num + 1}`.padStart(2, "0").slice(-2);
10
- if ("hours" === unit && 23 === num) return "00";
11
- if (num < 59) return `${num + 1}`.padStart(2, "0").slice(-2);
12
- if (59 === num) return "00";
13
- return value;
17
+ const newValue = num >= unitMaxValue[unit] ? 0 : num + 1;
18
+ return `${newValue}`.padStart(unitLength(unit), "0");
14
19
  }
15
20
  function decrementTimeUnitValue(unit, value) {
16
21
  const num = parseInt(value);
17
- if ("hours" === unit && num > 0) return `${num - 1}`.padStart(2, "0").slice(-2);
18
- if ("hours" === unit && 0 === num) return "23";
19
- if (num > 0) return `${num - 1}`.padStart(2, "0").slice(-2);
20
- if (0 === num) return "59";
21
- return value;
22
+ const newValue = num <= 0 ? unitMaxValue[unit] : num - 1;
23
+ return `${newValue}`.padStart(unitLength(unit), "0");
22
24
  }
23
25
  function updateTimeString(timeString, unit, value) {
24
- const newTimeString = "hours" === unit ? value.concat(timeString.slice(2)) : "minutes" === unit ? timeString.slice(0, 3).concat(value).concat(timeString.slice(5)) : timeString.slice(0, 6).concat(value);
25
- if (isValidTimeString(newTimeString)) return newTimeString;
26
+ const newTimeString = "hours" === unit ? value.concat(timeString.slice(2)) : "minutes" === unit ? timeString.slice(0, 3).concat(value).concat(timeString.slice(5)) : "seconds" === unit ? timeString.slice(0, 6).concat(value).concat(timeString.slice(8)) : timeString.slice(0, 9).concat(value);
27
+ if (isValidTimeString(newTimeString) || isValidTimeStringMillis(newTimeString)) return newTimeString;
26
28
  throw Error(`[date-utils] udateTimeSting invalid result ${newTimeString}`);
27
29
  }
28
- const validTimePattern = /(?:[0-1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]/;
30
+ const validTimePattern = /^(?:[0-1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/;
31
+ const validTimeMillisPattern = /^(?:[0-1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]\.[0-9]{3}$/;
29
32
  const isValidTimeString = (value)=>"string" == typeof value && validTimePattern.test(value);
33
+ const isValidTimeStringMillis = (value)=>"string" == typeof value && validTimeMillisPattern.test(value);
34
+ function normaliseTimeString(value, milliseconds = false) {
35
+ if (isValidTimeString(value)) return milliseconds ? `${value}.000` : value;
36
+ if (isValidTimeStringMillis(value)) return milliseconds ? value : value.slice(0, 8);
37
+ }
30
38
  function asTimeString(value, allowUndefined = false) {
31
39
  if (void 0 === value) if (allowUndefined) return value;
32
40
  else throw Error("[date-utils] asTimeString, value cannot be undefined");
33
41
  if (isValidTimeString(value)) return value;
42
+ if (isValidTimeStringMillis(value)) return value.slice(0, 8);
34
43
  if ("number" == typeof value) return Time.millisToTimeString(value);
35
44
  if ("string" == typeof value) {
36
45
  const valueAsInt = parseInt(value);
37
46
  if (!isNaN(valueAsInt)) return Time.millisToTimeString(valueAsInt);
38
47
  } else throw Error(`[date-utils] asTimeString, value ${value} is not valid TimeString`);
39
48
  }
49
+ function asTimeStringMillis(value, allowUndefined = false) {
50
+ if (void 0 === value) if (allowUndefined) return value;
51
+ else throw Error("[date-utils] asTimeStringMillis, value cannot be undefined");
52
+ const timeString = normaliseTimeString(value, true);
53
+ if (timeString) return timeString;
54
+ if ("number" == typeof value) return Time.millisToTimeStringMillis(value);
55
+ if ("string" == typeof value && /^\d+$/.test(value)) return Time.millisToTimeStringMillis(parseInt(value));
56
+ throw Error(`[date-utils] asTimeStringMillis, value ${value} is not valid TimeStringMillis`);
57
+ }
40
58
  const padZero = (val)=>`${val}`.padStart(2, "0");
41
59
  class TimeImpl {
42
60
  #hours;
43
61
  #minutes;
44
62
  #seconds;
63
+ #milliseconds;
45
64
  constructor(timeString){
46
- const [hours, minutes, seconds] = timeString.split(":");
65
+ const [hours, minutes, secondsAndMillis] = timeString.split(":");
66
+ const [seconds, milliseconds] = secondsAndMillis.split(".");
47
67
  this.#hours = parseInt(hours);
48
68
  this.#minutes = parseInt(minutes);
49
69
  this.#seconds = parseInt(seconds);
70
+ this.#milliseconds = void 0 === milliseconds ? void 0 : parseInt(milliseconds);
50
71
  }
51
72
  get hours() {
52
73
  return this.#hours;
@@ -57,24 +78,35 @@ class TimeImpl {
57
78
  get seconds() {
58
79
  return this.#seconds;
59
80
  }
81
+ get milliseconds() {
82
+ return this.#milliseconds ?? 0;
83
+ }
60
84
  asDate(date) {
61
85
  const dt = void 0 === date ? new Date() : "string" == typeof date ? new Date(date) : date;
62
86
  dt.setHours(this.#hours);
63
87
  dt.setMinutes(this.#minutes);
64
88
  dt.setSeconds(this.seconds);
65
- dt.setMilliseconds(0);
89
+ dt.setMilliseconds(this.milliseconds);
66
90
  return dt;
67
91
  }
68
92
  toString() {
69
- return Time.toString(this.#hours, this.#minutes, this.#seconds);
93
+ return Time.toString(this.#hours, this.#minutes, this.#seconds, this.#milliseconds);
70
94
  }
71
95
  }
72
96
  const Time = (timeString)=>new TimeImpl(timeString);
73
97
  Time.millisToTimeString = (timestamp)=>new Date(timestamp).toTimeString().slice(0, 8);
74
- Time.toString = (hours, minutes, seconds)=>`${padZero(hours)}:${padZero(minutes)}:${padZero(seconds)}`;
98
+ Time.millisToTimeStringMillis = (timestamp)=>{
99
+ const date = new Date(timestamp);
100
+ return `${date.toTimeString().slice(0, 8)}.${`${date.getMilliseconds()}`.padStart(3, "0")}`;
101
+ };
102
+ function timeToString(hours, minutes, seconds, milliseconds) {
103
+ const timeString = `${padZero(hours)}:${padZero(minutes)}:${padZero(seconds)}`;
104
+ return void 0 === milliseconds ? timeString : `${timeString}.${`${milliseconds}`.padStart(3, "0")}`;
105
+ }
106
+ Time.toString = timeToString;
75
107
  Time.isDateInMillis = (dtInMillis)=>{
76
108
  const n = "number" == typeof dtInMillis ? dtInMillis : Number(dtInMillis);
77
109
  if (!Number.isFinite(n) || !Number.isInteger(n)) return false;
78
110
  return new Date(n).getTime() === n;
79
111
  };
80
- export { Time, asTimeString, decrementTimeUnitValue, incrementTimeUnitValue, isValidTimeString, toCalendarDate, updateTimeString, zeroTime, zeroTimeUnit };
112
+ export { Time, asTimeString, asTimeStringMillis, decrementTimeUnitValue, incrementTimeUnitValue, isValidTimeString, isValidTimeStringMillis, normaliseTimeString, toCalendarDate, updateTimeString, zeroTime, zeroTimeMillis, zeroTimeUnit };
@@ -1,4 +1,4 @@
1
- import { isTypeDescriptor } from "../column-utils.js";
1
+ import { getTemporalInfo } from "./temporal.js";
2
2
  import { isDateTimePattern } from "./types.js";
3
3
  const defaultPatternsByType = {
4
4
  time: "hh:mm:ss",
@@ -8,10 +8,38 @@ const fallbackDateTimePattern = {
8
8
  date: defaultPatternsByType["date"],
9
9
  time: defaultPatternsByType["time"]
10
10
  };
11
- function dateTimePattern(type) {
12
- if (isTypeDescriptor(type)) {
11
+ const defaultDateTimePatternByKind = {
12
+ date: {
13
+ date: defaultPatternsByType.date
14
+ },
15
+ datetime: fallbackDateTimePattern,
16
+ time: {
17
+ time: defaultPatternsByType.time
18
+ }
19
+ };
20
+ const kindFromType = (type)=>{
21
+ const typeName = "string" == typeof type ? type : type?.name;
22
+ return "time" === typeName ? "time" : "date" === typeName ? "date" : "date/time" === typeName ? "datetime" : void 0;
23
+ };
24
+ function dateTimePattern(type, kind = kindFromType(type)) {
25
+ if ("object" == typeof type && null !== type) {
13
26
  if (type.formatting && isDateTimePattern(type.formatting.pattern)) return type.formatting.pattern;
14
27
  }
15
- return fallbackDateTimePattern;
28
+ return kind ? defaultDateTimePatternByKind[kind] : fallbackDateTimePattern;
16
29
  }
17
- export { dateTimePattern, defaultPatternsByType, fallbackDateTimePattern };
30
+ const withDateTimePattern = (descriptor, pattern)=>{
31
+ if (void 0 === pattern || void 0 === getTemporalInfo(descriptor)) return descriptor;
32
+ const name = pattern.date && pattern.time ? "date/time" : pattern.date ? "date" : "time";
33
+ const formatting = "object" == typeof descriptor.type ? descriptor.type.formatting : void 0;
34
+ return {
35
+ ...descriptor,
36
+ type: {
37
+ name,
38
+ formatting: {
39
+ ...formatting,
40
+ pattern
41
+ }
42
+ }
43
+ };
44
+ };
45
+ export { dateTimePattern, defaultDateTimePatternByKind, defaultPatternsByType, fallbackDateTimePattern, withDateTimePattern };
@@ -1,4 +1,6 @@
1
1
  import { DateFormatter } from "@internationalized/date";
2
+ import { EpochTimestamp } from "./EpochTimestamp.js";
3
+ import { resolveTimeZone } from "./time-zone.js";
2
4
  const baseTimeFormatOptions = {
3
5
  hour: "2-digit",
4
6
  minute: "2-digit",
@@ -23,8 +25,7 @@ const formatConfigByTimePatterns = {
23
25
  locale: "en-GB",
24
26
  options: {
25
27
  ...baseTimeFormatOptions,
26
- hour12: false,
27
- fractionalSecondDigits: 3
28
+ hour12: false
28
29
  }
29
30
  }
30
31
  };
@@ -81,21 +82,58 @@ const formatConfigByDatePatterns = {
81
82
  }
82
83
  }
83
84
  };
84
- const dateFormatterISO = {
85
- format: (date)=>new Date(date - 60000 * date.getTimezoneOffset()).toISOString().replace(/T.*/, "")
85
+ const NARROW_NBSP = /\u202f/g;
86
+ const intlFormatterCache = new Map();
87
+ const getIntlFormatter = (locale, options)=>{
88
+ const key = `${locale}:${JSON.stringify(options)}`;
89
+ let formatter = intlFormatterCache.get(key);
90
+ if (void 0 === formatter) {
91
+ formatter = new Intl.DateTimeFormat(locale, options);
92
+ intlFormatterCache.set(key, formatter);
93
+ }
94
+ return formatter;
86
95
  };
87
- function getDateFormatter({ locale, options }) {
88
- return new DateFormatter(locale, options);
96
+ const isoDateFormatter = (timeZone)=>{
97
+ const formatter = getIntlFormatter("en-GB", {
98
+ ...baseDateFormatOptions,
99
+ month: "2-digit",
100
+ timeZone
101
+ });
102
+ return (ts)=>{
103
+ const parts = formatter.formatToParts(ts.toDate());
104
+ const get = (type)=>parts.find((p)=>p.type === type)?.value ?? "";
105
+ return `${get("year")}-${get("month")}-${get("day")}`;
106
+ };
107
+ };
108
+ const dateFormatter = (pattern, timeZone, localeOverride)=>{
109
+ const { locale, options } = formatConfigByDatePatterns[pattern];
110
+ const formatter = getIntlFormatter(localeOverride ?? locale, {
111
+ ...options,
112
+ timeZone
113
+ });
114
+ return (ts)=>formatter.format(ts.toDate());
115
+ };
116
+ const timeFormatter = (pattern, timeZone, fractionalSecondDigits, localeOverride)=>{
117
+ const { locale, options } = formatConfigByTimePatterns[pattern];
118
+ const formatter = getIntlFormatter(localeOverride ?? locale, {
119
+ ...options,
120
+ timeZone
121
+ });
122
+ return (ts)=>formatter.formatToParts(ts.toDate()).map(({ type, value })=>"second" === type && fractionalSecondDigits > 0 ? `${value}.${ts.fractionalSecond(fractionalSecondDigits)}` : value.replace(NARROW_NBSP, " ")).join("");
123
+ };
124
+ function formatTimestamp({ date, time }, { fractionalSecondDigits = "hh:mm:ss.ms" === time ? 3 : 0, locale, timeZone } = {}) {
125
+ const tz = resolveTimeZone(timeZone);
126
+ const formatters = [];
127
+ if ("yyyy-mm-dd" === date) formatters.push(isoDateFormatter(tz));
128
+ else if (date) formatters.push(dateFormatter(date, tz, locale));
129
+ if (time) formatters.push(timeFormatter(time, tz, fractionalSecondDigits, locale));
130
+ return (ts)=>formatters.map((f)=>f(ts)).join(" ");
89
131
  }
90
- function getDateAndTimeFormatters({ date, time }) {
91
- const out = [];
92
- if ("yyyy-mm-dd" === date) out.push(dateFormatterISO);
93
- else if (date) out.push(getDateFormatter(formatConfigByDatePatterns[date]));
94
- if (time) out.push(getDateFormatter(formatConfigByTimePatterns[time]));
95
- return out;
132
+ function formatDate(pattern, options) {
133
+ const formatter = formatTimestamp(pattern, options);
134
+ return (d)=>formatter(EpochTimestamp.fromDate(d));
96
135
  }
97
- function formatDate(pattern) {
98
- const formatters = getDateAndTimeFormatters(pattern);
99
- return (d)=>formatters.map((f)=>f.format(d)).join(" ");
136
+ function getDateFormatter({ locale, options }) {
137
+ return new DateFormatter(locale, options);
100
138
  }
101
- export { formatDate, getDateFormatter };
139
+ export { formatDate, formatTimestamp, getDateFormatter };
package/src/date/index.js CHANGED
@@ -1,4 +1,7 @@
1
1
  export * from "./formatter.js";
2
- export { Time, asTimeString, decrementTimeUnitValue, incrementTimeUnitValue, isValidTimeString, toCalendarDate, updateTimeString, zeroTime, zeroTimeUnit } from "./date-utils.js";
3
- export { dateTimePattern, defaultPatternsByType, fallbackDateTimePattern } from "./dateTimePattern.js";
2
+ export * from "./temporal.js";
3
+ export * from "./time-zone.js";
4
+ export { Time, asTimeString, asTimeStringMillis, decrementTimeUnitValue, incrementTimeUnitValue, isValidTimeString, isValidTimeStringMillis, normaliseTimeString, toCalendarDate, updateTimeString, zeroTime, zeroTimeMillis, zeroTimeUnit } from "./date-utils.js";
5
+ export { dateTimePattern, defaultDateTimePatternByKind, defaultPatternsByType, fallbackDateTimePattern, withDateTimePattern } from "./dateTimePattern.js";
4
6
  export { dateTimeLabelByType, isDatePattern, isTimePattern, supportedDateTimePatterns } from "./types.js";
7
+ export { EpochTimestamp } from "./EpochTimestamp.js";