@harperfast/harper 5.2.1 → 5.2.2

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 (117) hide show
  1. package/bin/copyDb.ts +19 -7
  2. package/components/deploymentRecorder.ts +102 -11
  3. package/components/operations.js +4 -0
  4. package/config/configUtils.ts +66 -11
  5. package/config/harperConfigEnvVars.ts +114 -17
  6. package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -4
  7. package/dist/bin/copyDb.js +20 -7
  8. package/dist/bin/copyDb.js.map +1 -1
  9. package/dist/components/deploymentRecorder.d.ts +6 -0
  10. package/dist/components/deploymentRecorder.js +94 -12
  11. package/dist/components/deploymentRecorder.js.map +1 -1
  12. package/dist/components/operations.js +4 -0
  13. package/dist/components/operations.js.map +1 -1
  14. package/dist/config/configUtils.d.ts +6 -0
  15. package/dist/config/configUtils.js +65 -9
  16. package/dist/config/configUtils.js.map +1 -1
  17. package/dist/config/harperConfigEnvVars.js +108 -16
  18. package/dist/config/harperConfigEnvVars.js.map +1 -1
  19. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -4
  20. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js.map +1 -1
  21. package/dist/resources/DatabaseTransaction.d.ts +2 -0
  22. package/dist/resources/DatabaseTransaction.js +6 -5
  23. package/dist/resources/DatabaseTransaction.js.map +1 -1
  24. package/dist/resources/Table.d.ts +2 -1
  25. package/dist/resources/Table.js +66 -16
  26. package/dist/resources/Table.js.map +1 -1
  27. package/dist/resources/auditStore.js +27 -1
  28. package/dist/resources/auditStore.js.map +1 -1
  29. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +15 -2
  30. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +128 -39
  31. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  32. package/dist/resources/search.d.ts +1 -1
  33. package/dist/resources/search.js +7 -3
  34. package/dist/resources/search.js.map +1 -1
  35. package/dist/server/threads/manageThreads.js +8 -0
  36. package/dist/server/threads/manageThreads.js.map +1 -1
  37. package/dist/sqlEngine/parser/normalizer.js +37 -9
  38. package/dist/sqlEngine/parser/normalizer.js.map +1 -1
  39. package/dist/utility/environment/environmentManager.d.ts +11 -0
  40. package/dist/utility/environment/environmentManager.js +154 -0
  41. package/dist/utility/environment/environmentManager.js.map +1 -1
  42. package/dist/utility/lmdb/environmentUtility.js +2 -1
  43. package/dist/utility/lmdb/environmentUtility.js.map +1 -1
  44. package/npm-shrinkwrap.json +13 -7
  45. package/package.json +2 -2
  46. package/resources/DESIGN.md +1 -0
  47. package/resources/DatabaseTransaction.ts +7 -5
  48. package/resources/Table.ts +77 -16
  49. package/resources/auditStore.ts +32 -1
  50. package/resources/indexes/HierarchicalNavigableSmallWorld.ts +119 -45
  51. package/resources/search.ts +8 -3
  52. package/server/threads/manageThreads.js +8 -0
  53. package/studio/web/assets/{Chat-aApwhRmz.js → Chat-BMUjPklt.js} +5 -5
  54. package/studio/web/assets/{Chat-aApwhRmz.js.map → Chat-BMUjPklt.js.map} +1 -1
  55. package/studio/web/assets/{FloatingChat-sC0H91n9.js → FloatingChat-Cbghuxe5.js} +5 -5
  56. package/studio/web/assets/{FloatingChat-sC0H91n9.js.map → FloatingChat-Cbghuxe5.js.map} +1 -1
  57. package/studio/web/assets/{apiToken-DZ8JhHOI.js → apiToken-CkgTQAqc.js} +2 -2
  58. package/studio/web/assets/{apiToken-DZ8JhHOI.js.map → apiToken-CkgTQAqc.js.map} +1 -1
  59. package/studio/web/assets/{applications-9SEVLIO5.js → applications-WvF4NOTN.js} +13 -13
  60. package/studio/web/assets/applications-WvF4NOTN.js.map +1 -0
  61. package/studio/web/assets/chevron-right-anDJCGlp.js +2 -0
  62. package/studio/web/assets/chevron-right-anDJCGlp.js.map +1 -0
  63. package/studio/web/assets/index-CDeqVx71.css +1 -0
  64. package/studio/web/assets/index-Sevt3MVx.js +819 -0
  65. package/studio/web/assets/index-Sevt3MVx.js.map +1 -0
  66. package/studio/web/assets/index.lazy-Dg5YYPGg.js +14 -0
  67. package/studio/web/assets/{index.lazy-CN1zq4I4.js.map → index.lazy-Dg5YYPGg.js.map} +1 -1
  68. package/studio/web/assets/{jsonMode-Cjj7dtlr.js → jsonMode-CBSEleE7.js} +2 -2
  69. package/studio/web/assets/{jsonMode-Cjj7dtlr.js.map → jsonMode-CBSEleE7.js.map} +1 -1
  70. package/studio/web/assets/languageServices-gxedeeTo.js +2 -0
  71. package/studio/web/assets/languageServices-gxedeeTo.js.map +1 -0
  72. package/studio/web/assets/{notifications-BFAF07xr.js → notifications-Ap84YyQp.js} +2 -2
  73. package/studio/web/assets/{notifications-BFAF07xr.js.map → notifications-Ap84YyQp.js.map} +1 -1
  74. package/studio/web/assets/notifications-vehqyOLM.js +2 -0
  75. package/studio/web/assets/{notifications-CKlYVvVN.js.map → notifications-vehqyOLM.js.map} +1 -1
  76. package/studio/web/assets/{pollUnlessForbidden-HHdQZW1N.js → pollUnlessForbidden-BhzefVRn.js} +2 -2
  77. package/studio/web/assets/pollUnlessForbidden-BhzefVRn.js.map +1 -0
  78. package/studio/web/assets/{profile-A1zhEdFG.js → profile-DzDpRLHY.js} +2 -2
  79. package/studio/web/assets/{profile-A1zhEdFG.js.map → profile-DzDpRLHY.js.map} +1 -1
  80. package/studio/web/assets/queryClient-CgtkoiCY.js +2 -0
  81. package/studio/web/assets/queryClient-CgtkoiCY.js.map +1 -0
  82. package/studio/web/assets/regions-DpJ8s7iN.js +2 -0
  83. package/studio/web/assets/regions-DpJ8s7iN.js.map +1 -0
  84. package/studio/web/assets/register-BDy01-q3.js +3 -0
  85. package/studio/web/assets/register-BDy01-q3.js.map +1 -0
  86. package/studio/web/assets/register-CQHRwNP4.js +3 -0
  87. package/studio/web/assets/register-CQHRwNP4.js.map +1 -0
  88. package/studio/web/assets/{setComponentFile-SEtBt_GV.js → setComponentFile-D0aPcI3W.js} +2 -2
  89. package/studio/web/assets/{setComponentFile-SEtBt_GV.js.map → setComponentFile-D0aPcI3W.js.map} +1 -1
  90. package/studio/web/assets/setup-I15FAF9H.js +3 -0
  91. package/studio/web/assets/{setup-BisINqdH.js.map → setup-I15FAF9H.js.map} +1 -1
  92. package/studio/web/assets/status-BF1eJshJ.js +62 -0
  93. package/studio/web/assets/status-BF1eJshJ.js.map +1 -0
  94. package/studio/web/assets/{swagger-ui-react-CAi_s1PC.js → swagger-ui-react-DwSPXXhl.js} +2 -2
  95. package/studio/web/assets/{swagger-ui-react-CAi_s1PC.js.map → swagger-ui-react-DwSPXXhl.js.map} +1 -1
  96. package/studio/web/assets/{textarea-DhLMZ3PA.js → table-DpOjNQHt.js} +2 -10
  97. package/studio/web/assets/table-DpOjNQHt.js.map +1 -0
  98. package/studio/web/assets/{tsMode-DT74tlkM.js → tsMode-Bu51A-VG.js} +2 -2
  99. package/studio/web/assets/{tsMode-DT74tlkM.js.map → tsMode-Bu51A-VG.js.map} +1 -1
  100. package/studio/web/assets/{useEntityRestURL-CGRGc1n7.js → useEntityRestURL-DrV1zMrN.js} +2 -2
  101. package/studio/web/assets/{useEntityRestURL-CGRGc1n7.js.map → useEntityRestURL-DrV1zMrN.js.map} +1 -1
  102. package/studio/web/index.html +6 -7
  103. package/utility/environment/environmentManager.ts +152 -0
  104. package/utility/lmdb/environmentUtility.ts +2 -1
  105. package/studio/web/assets/applications-9SEVLIO5.js.map +0 -1
  106. package/studio/web/assets/index-C8Mv9kHV.css +0 -1
  107. package/studio/web/assets/index-Dy3uDGXb.js +0 -809
  108. package/studio/web/assets/index-Dy3uDGXb.js.map +0 -1
  109. package/studio/web/assets/index.lazy-CN1zq4I4.js +0 -14
  110. package/studio/web/assets/notifications-CKlYVvVN.js +0 -2
  111. package/studio/web/assets/pollUnlessForbidden-HHdQZW1N.js.map +0 -1
  112. package/studio/web/assets/queryClient-CbA8wM7J.js +0 -2
  113. package/studio/web/assets/queryClient-CbA8wM7J.js.map +0 -1
  114. package/studio/web/assets/setup-BisINqdH.js +0 -3
  115. package/studio/web/assets/status-BuQoCc7l.js +0 -62
  116. package/studio/web/assets/status-BuQoCc7l.js.map +0 -1
  117. package/studio/web/assets/textarea-DhLMZ3PA.js.map +0 -1
@@ -341,6 +341,7 @@ function makeTable(options) {
341
341
  if (!properties)
342
342
  properties = (0, jsonSchemaTypes_ts_1.projectAttributesToProperties)(attributes);
343
343
  const updateRecord = (0, RecordEncoder_ts_1.recordUpdater)(primaryStore, tableId, auditStore);
344
+ let warnedNullSourcePut = false; // latched: one warn per table per worker (see _writeUpdate)
344
345
  let sourceLoad; // if a source has a load function (replicator), record it here
345
346
  let hasSourceGet;
346
347
  let primaryKeyAttribute;
@@ -2157,6 +2158,19 @@ function makeTable(options) {
2157
2158
  const context = this.getContext();
2158
2159
  const transaction = txnForContext(context);
2159
2160
  checkValidId(id);
2161
+ if (fullUpdate && recordUpdate == null && options?.isNotification) {
2162
+ // A source/replication-applied put must carry the record; these applies skip record
2163
+ // validation, so this is the one path a nullish full update reaches (isNotification scopes
2164
+ // this to the apply dispatcher, not instance flows that fill from #changes). Applying it
2165
+ // stores nothing and mints an audit-only entry misrepresenting the write (#2153) — skip it;
2166
+ // a redelivery re-skips and a later real write supersedes.
2167
+ logger_ts_1.logger.trace?.('Skipped valueless source put', tableName, id, options?.nodeId);
2168
+ if (!warnedNullSourcePut) {
2169
+ warnedNullSourcePut = true;
2170
+ logger_ts_1.logger.warn?.(`Skipping a source-applied put with no record content for ${tableName} id ${id} from node ${options?.nodeId}`, new Error('valueless source put'));
2171
+ }
2172
+ return;
2173
+ }
2160
2174
  const entry = this.#entry ?? primaryStore.getEntry(id, { transaction: transaction.getReadTxn() });
2161
2175
  const writeToSource = () => {
2162
2176
  if (!this.constructor.source || context?.source)
@@ -3186,6 +3200,13 @@ function makeTable(options) {
3186
3200
  if (conditions.length > 0 || operator)
3187
3201
  conditions = prepareConditions(conditions, operator);
3188
3202
  const sort = typeof target.sort === 'object' && target.sort;
3203
+ for (let order = sort; order; order = order.next) {
3204
+ if (typeof order.attribute !== 'string')
3205
+ continue;
3206
+ const customIndex = indices[order.attribute]?.customIndex;
3207
+ if (customIndex?.exactDistance)
3208
+ customIndex.exactDistance(order, null);
3209
+ }
3189
3210
  let postOrdering;
3190
3211
  if (sort) {
3191
3212
  // TODO: Support index-assisted sorts of unions, which will require potentially recursively adding/modifying an order aligned condition and be able to recursively undo it if necessary
@@ -3223,9 +3244,12 @@ function makeTable(options) {
3223
3244
  if (sort.next) {
3224
3245
  postOrdering = {
3225
3246
  dbOrderedAttribute: sort.attribute,
3247
+ dbOrderedSort: sort,
3226
3248
  attribute: sort.next.attribute,
3227
3249
  descending: sort.next.descending,
3228
3250
  next: sort.next.next,
3251
+ target: sort.next.target,
3252
+ distance: sort.next.distance,
3229
3253
  };
3230
3254
  }
3231
3255
  }
@@ -3290,7 +3314,7 @@ function makeTable(options) {
3290
3314
  // since those never run the ensureLoaded-gated freshness check this transform
3291
3315
  // otherwise applies unconditionally to every read.
3292
3316
  const includeExpired = target.includeExpired === true;
3293
- const transformToRecord = TableResource.transformEntryForSelect(select, context, readTxn, filtered, ensure_loaded, true, boundRowFilter, includeExpired);
3317
+ const transformToRecord = TableResource.transformEntryForSelect(select, context, readTxn, filtered, ensure_loaded, true, boundRowFilter, includeExpired, postOrdering);
3294
3318
  let results = TableResource.transformToOrderedSelect(entries, select, postOrdering, context, readTxn, transformToRecord);
3295
3319
  // apply any offset/limit after all the sorting and filtering
3296
3320
  if (target.offset || target.limit !== undefined)
@@ -3332,10 +3356,9 @@ function makeTable(options) {
3332
3356
  function createComparator(order) {
3333
3357
  const nextComparator = order.next && createComparator(order.next);
3334
3358
  const descending = order.descending;
3335
- context.sort = order; // make sure this is set to the current sort order
3336
3359
  return (entryA, entryB) => {
3337
- const a = getAttributeValue(entryA, order.attribute, context);
3338
- const b = getAttributeValue(entryB, order.attribute, context);
3360
+ const a = getAttributeValue(entryA, order.attribute, context, order);
3361
+ const b = getAttributeValue(entryB, order.attribute, context, order);
3339
3362
  const diff = descending
3340
3363
  ? (0, ordered_binary_1.compareKeys)(convertToComparableKeys(b), convertToComparableKeys(a))
3341
3364
  : (0, ordered_binary_1.compareKeys)(convertToComparableKeys(a), convertToComparableKeys(b));
@@ -3385,7 +3408,7 @@ function makeTable(options) {
3385
3408
  // if the index has already provided the first order of sorting, we only need to sort
3386
3409
  // within each grouping
3387
3410
  if (dbOrderedAttribute) {
3388
- const groupingValue = getAttributeValue(entry, dbOrderedAttribute, context);
3411
+ const groupingValue = getAttributeValue(entry, dbOrderedAttribute, context, sort.dbOrderedSort);
3389
3412
  if (firstEntry) {
3390
3413
  firstEntry = false;
3391
3414
  lastGroupingValue = groupingValue;
@@ -3495,9 +3518,10 @@ function makeTable(options) {
3495
3518
  * authorization verdict can't be made on bytes that differ from what the caller receives.
3496
3519
  * @param includeExpired when true, a row past its TTL but not yet swept is treated as a live
3497
3520
  * match rather than gone (used by the SQL engine's UPDATE/DELETE row-finder).
3521
+ * @param sort post-ordering owned by this selection
3498
3522
  * @returns
3499
3523
  */
3500
- static transformEntryForSelect(select, context, readTxn, filtered, ensure_loaded, canSkip, rowFilter, includeExpired) {
3524
+ static transformEntryForSelect(select, context, readTxn, filtered, ensure_loaded, canSkip, rowFilter, includeExpired, sort) {
3501
3525
  let checkLoaded;
3502
3526
  if (ensure_loaded &&
3503
3527
  hasSourceGet &&
@@ -3566,7 +3590,7 @@ function makeTable(options) {
3566
3590
  swrResource.setRecord(record);
3567
3591
  const loadingFromSource = ensureLoadedFromSource(source, entry.key ?? entry, entry, context, swrResource);
3568
3592
  if (loadingFromSource?.then) {
3569
- return loadingFromSource.then(transform);
3593
+ return loadingFromSource.then(transform.bind(this));
3570
3594
  }
3571
3595
  }
3572
3596
  }
@@ -3603,7 +3627,7 @@ function makeTable(options) {
3603
3627
  }
3604
3628
  }
3605
3629
  else {
3606
- value = resolver(record, context, entry, true);
3630
+ value = resolver(record, context, entry, true, sort);
3607
3631
  }
3608
3632
  const handleResolvedValue = (value) => {
3609
3633
  if (resolver.directReturn)
@@ -3621,7 +3645,7 @@ function makeTable(options) {
3621
3645
  // otherwise pass along the nested selected
3622
3646
  attribute_name === attribute
3623
3647
  ? null
3624
- : attribute.select || (Array.isArray(attribute) ? attribute : null), context, targetReadTxn, filterMap, ensure_loaded));
3648
+ : attribute.select || (Array.isArray(attribute) ? attribute : null), context, targetReadTxn, filterMap, ensure_loaded, undefined, undefined, undefined, typeof attribute.sort === 'object' && attribute.sort));
3625
3649
  if (Array.isArray(value)) {
3626
3650
  const results = [];
3627
3651
  const iterator = targetTable
@@ -4679,8 +4703,26 @@ function makeTable(options) {
4679
4703
  $updatedTime: (object, context, entry) => entry.version,
4680
4704
  $expiresAt: (object, context, entry) => entry.expiresAt,
4681
4705
  $record: (object, context, entry) => (entry ? { value: object } : object),
4682
- $distance: (object, context, entry) => {
4683
- return entry && (entry.distance ?? context?.vectorDistances?.get(entry));
4706
+ $distance: (object, context, entry, returnEntry, sort) => {
4707
+ if (!entry)
4708
+ return;
4709
+ if (entry.distance !== undefined)
4710
+ return entry.distance;
4711
+ let distanceSort = sort;
4712
+ while (distanceSort &&
4713
+ (typeof distanceSort.attribute !== 'string' ||
4714
+ !Array.isArray(distanceSort.target) ||
4715
+ !indices[distanceSort.attribute]?.customIndex?.propertyResolver))
4716
+ distanceSort = distanceSort.next;
4717
+ if (!distanceSort)
4718
+ return;
4719
+ const customIndex = indices[distanceSort.attribute].customIndex;
4720
+ const vector = object[distanceSort.attribute];
4721
+ const distanceCache = context?.vectorDistanceCaches?.get(distanceSort);
4722
+ const cachedDistance = distanceCache?.get(entry) ?? distanceCache?.get(vector);
4723
+ if (cachedDistance !== undefined)
4724
+ return cachedDistance;
4725
+ return customIndex.propertyResolver(vector, context, entry, distanceSort);
4684
4726
  },
4685
4727
  };
4686
4728
  for (const attribute of this.attributes) {
@@ -4820,9 +4862,13 @@ function makeTable(options) {
4820
4862
  }
4821
4863
  else if (indices[attribute.name]?.customIndex?.propertyResolver) {
4822
4864
  const customIndex = indices[attribute.name].customIndex;
4823
- propertyResolvers[attribute.name] = (object, context, entry) => {
4865
+ propertyResolvers[attribute.name] = (object, context, entry, returnEntry, sort, comparing) => {
4824
4866
  const value = object[attribute.name];
4825
- return customIndex.propertyResolver(value, context, entry);
4867
+ const sortAttribute = sort?.attribute;
4868
+ const resolvesSort = comparing === true &&
4869
+ (sortAttribute === attribute.name ||
4870
+ (Array.isArray(sortAttribute) && sortAttribute[sortAttribute.length - 1] === attribute.name));
4871
+ return customIndex.propertyResolver(value, context, entry, resolvesSort ? sort : undefined);
4826
4872
  };
4827
4873
  propertyResolvers[attribute.name].directReturn = true;
4828
4874
  }
@@ -5386,6 +5432,7 @@ function makeTable(options) {
5386
5432
  // Inherit never-drop-on-conflict so a source-applied multi-store transaction doesn't
5387
5433
  // drop the canonical write when a secondary store hits a transient conflict.
5388
5434
  transaction.next.sourceApply = transaction.sourceApply;
5435
+ transaction.next.timeoutBudget = transaction.timeoutBudget;
5389
5436
  // Inherit the replay marker so a multi-table replay transaction skips validation on
5390
5437
  // every store, not just the first (harper#1316).
5391
5438
  transaction.next.isReplay = transaction.isReplay;
@@ -5417,7 +5464,7 @@ function makeTable(options) {
5417
5464
  return transaction;
5418
5465
  }
5419
5466
  }
5420
- function getAttributeValue(entry, attribute_name, context) {
5467
+ function getAttributeValue(entry, attribute_name, context, sort) {
5421
5468
  if (!entry) {
5422
5469
  return;
5423
5470
  }
@@ -5429,14 +5476,17 @@ function makeTable(options) {
5429
5476
  for (let i = 0, l = attribute_name.length; i < l; i++) {
5430
5477
  const attribute = attribute_name[i];
5431
5478
  const resolver = resolvers?.[attribute];
5432
- value = resolver && value ? resolver(value, context, entry) : value?.[attribute];
5479
+ value =
5480
+ resolver && value
5481
+ ? resolver(value, context, entry, false, i === l - 1 ? sort : undefined, true)
5482
+ : value?.[attribute];
5433
5483
  entry = null; // can't use this in the nested object
5434
5484
  resolvers = resolver?.definition?.tableClass?.propertyResolvers;
5435
5485
  }
5436
5486
  return value;
5437
5487
  }
5438
5488
  const resolver = propertyResolvers[attribute_name];
5439
- return resolver ? resolver(record, context, entry) : record[attribute_name];
5489
+ return resolver ? resolver(record, context, entry, false, sort, true) : record[attribute_name];
5440
5490
  }
5441
5491
  function transformToEntries(ids, select, context, readTxn, filters) {
5442
5492
  // TODO: Test and ensure that we break out of these loops when a connection is lost