@resistdesign/voltra 3.0.0-alpha.35 → 3.0.0-alpha.37

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/api/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { ITEM_RELATIONSHIP_DAC_RESOURCE_NAME, ComparisonOperators } from '../chunk-7AMEFPPP.js';
2
- import { getNoErrorDescriptor, getErrorDescriptor, ERROR_MESSAGE_CONSTANTS, validateTypeOperationAllowed, getValidityValue, validateTypeInfoValue, validateTypeInfoFieldValue } from '../chunk-WTD5BBJP.js';
3
- import { mergeStringPaths, getPathString, getPathArray } from '../chunk-GYWRAW3Y.js';
2
+ import { getNoErrorDescriptor, getErrorDescriptor, ERROR_MESSAGE_CONSTANTS, validateTypeOperationAllowed, getValidityValue, validateTypeInfoValue, validateTypeInfoFieldValue } from '../chunk-YCTVEW2I.js';
3
+ import { mergeStringPaths, getPathString, getPathArray } from '../chunk-WNFRDIBW.js';
4
4
  import '../chunk-I2KLQ2HA.js';
5
- import { QueryCommand, GetItemCommand, BatchGetItemCommand, BatchWriteItemCommand, DynamoDBClient, PutItemCommand, UpdateItemCommand, DeleteItemCommand, ScanCommand } from '@aws-sdk/client-dynamodb';
5
+ import { QueryCommand, PutItemCommand, ConditionalCheckFailedException, GetItemCommand, BatchGetItemCommand, BatchWriteItemCommand, DynamoDBClient, UpdateItemCommand, DeleteItemCommand, ScanCommand } from '@aws-sdk/client-dynamodb';
6
6
  import { marshall, unmarshall } from '@aws-sdk/util-dynamodb';
7
7
  import { S3, PutObjectCommand, HeadObjectCommand, CopyObjectCommand, GetObjectCommand, DeleteObjectCommand, ListObjectsV2Command } from '@aws-sdk/client-s3';
8
8
  import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
@@ -1171,6 +1171,25 @@ var createAwsSdkV3DynamoClient = (client) => ({
1171
1171
  Item: response.Item ? fromAwsKey(response.Item) : void 0
1172
1172
  };
1173
1173
  },
1174
+ putItem: async (input) => {
1175
+ try {
1176
+ await client.send(
1177
+ new PutItemCommand({
1178
+ TableName: input.TableName,
1179
+ Item: toAwsKey(input.Item),
1180
+ ConditionExpression: input.ConditionExpression,
1181
+ ExpressionAttributeNames: input.ExpressionAttributeNames,
1182
+ ExpressionAttributeValues: input.ExpressionAttributeValues ? toAwsKey(input.ExpressionAttributeValues) : void 0
1183
+ })
1184
+ );
1185
+ return {};
1186
+ } catch (error) {
1187
+ if (error instanceof ConditionalCheckFailedException) {
1188
+ return { conditionFailed: true };
1189
+ }
1190
+ throw error;
1191
+ }
1192
+ },
1174
1193
  query: async (input) => {
1175
1194
  const response = await client.send(
1176
1195
  new QueryCommand({
@@ -3324,18 +3343,53 @@ async function structuredHandler(event) {
3324
3343
  }
3325
3344
 
3326
3345
  // src/api/Indexing/structured/StructuredStringLike.ts
3327
- var MAX_INDEXED_STRING_LENGTH = 128;
3328
- var MAX_TOKENS_PER_VALUE = 256;
3329
- var MAX_NGRAM_SIZE = 3;
3346
+ var DEFAULT_MAX_INDEXED_STRING_LENGTH = 128;
3347
+ var DEFAULT_MAX_TOKENS_PER_VALUE = 256;
3348
+ var DEFAULT_MIN_NGRAM_SIZE = 1;
3349
+ var DEFAULT_MAX_NGRAM_SIZE = 3;
3330
3350
  var LIKE_WILDCARD_REGEX = /[%_]/;
3331
3351
  var NORMALIZED_WHITESPACE_REGEX = /\s+/g;
3332
3352
  var STRUCTURED_STRING_CONTAINS_TOKEN_PREFIX = "__str__:";
3353
+ var DEFAULT_STRUCTURED_STRING_TOKENIZER_CONFIG = {
3354
+ minNgramSize: DEFAULT_MIN_NGRAM_SIZE,
3355
+ maxNgramSize: DEFAULT_MAX_NGRAM_SIZE,
3356
+ maxIndexedStringLength: DEFAULT_MAX_INDEXED_STRING_LENGTH,
3357
+ maxTokensPerValue: DEFAULT_MAX_TOKENS_PER_VALUE
3358
+ };
3359
+ var clampInteger = (value, fallback, minimum = 1) => {
3360
+ if (typeof value !== "number" || !Number.isFinite(value)) {
3361
+ return fallback;
3362
+ }
3363
+ return Math.max(minimum, Math.floor(value));
3364
+ };
3365
+ var resolveStructuredStringTokenizerConfig = (config) => {
3366
+ const minNgramSize = clampInteger(
3367
+ config?.minNgramSize,
3368
+ DEFAULT_STRUCTURED_STRING_TOKENIZER_CONFIG.minNgramSize
3369
+ );
3370
+ const maxNgramSize = clampInteger(
3371
+ config?.maxNgramSize,
3372
+ DEFAULT_STRUCTURED_STRING_TOKENIZER_CONFIG.maxNgramSize
3373
+ );
3374
+ return {
3375
+ minNgramSize: Math.min(minNgramSize, maxNgramSize),
3376
+ maxNgramSize: Math.max(minNgramSize, maxNgramSize),
3377
+ maxIndexedStringLength: clampInteger(
3378
+ config?.maxIndexedStringLength,
3379
+ DEFAULT_STRUCTURED_STRING_TOKENIZER_CONFIG.maxIndexedStringLength
3380
+ ),
3381
+ maxTokensPerValue: clampInteger(
3382
+ config?.maxTokensPerValue,
3383
+ DEFAULT_STRUCTURED_STRING_TOKENIZER_CONFIG.maxTokensPerValue
3384
+ )
3385
+ };
3386
+ };
3333
3387
  var normalizeStructuredLikeString = (value) => value.toLowerCase().trim().replace(NORMALIZED_WHITESPACE_REGEX, " ");
3334
- var toNgrams = (normalized) => {
3388
+ var toNgrams = (normalized, config) => {
3335
3389
  const tokens = [];
3336
3390
  const seen = /* @__PURE__ */ new Set();
3337
- const limited = normalized.slice(0, MAX_INDEXED_STRING_LENGTH);
3338
- for (let size = 1; size <= MAX_NGRAM_SIZE; size += 1) {
3391
+ const limited = normalized.slice(0, config.maxIndexedStringLength);
3392
+ for (let size = config.minNgramSize; size <= config.maxNgramSize; size += 1) {
3339
3393
  if (limited.length < size) {
3340
3394
  break;
3341
3395
  }
@@ -3344,7 +3398,7 @@ var toNgrams = (normalized) => {
3344
3398
  if (!seen.has(token)) {
3345
3399
  seen.add(token);
3346
3400
  tokens.push(token);
3347
- if (tokens.length >= MAX_TOKENS_PER_VALUE) {
3401
+ if (tokens.length >= config.maxTokensPerValue) {
3348
3402
  return tokens;
3349
3403
  }
3350
3404
  }
@@ -3353,26 +3407,28 @@ var toNgrams = (normalized) => {
3353
3407
  return tokens;
3354
3408
  };
3355
3409
  var toContainsToken = (token) => `${STRUCTURED_STRING_CONTAINS_TOKEN_PREFIX}${token}`;
3356
- var buildStructuredStringContainsTokens = (value) => {
3410
+ var buildStructuredStringContainsTokens = (value, tokenizerConfig) => {
3411
+ const config = resolveStructuredStringTokenizerConfig(tokenizerConfig);
3357
3412
  const normalized = normalizeStructuredLikeString(value);
3358
3413
  if (!normalized.length) {
3359
3414
  return [];
3360
3415
  }
3361
- return toNgrams(normalized).map(toContainsToken);
3416
+ return toNgrams(normalized, config).map(toContainsToken);
3362
3417
  };
3363
- var buildStructuredLikePatternTokens = (value) => {
3418
+ var buildStructuredLikePatternTokens = (value, tokenizerConfig) => {
3419
+ const config = resolveStructuredStringTokenizerConfig(tokenizerConfig);
3364
3420
  const normalized = normalizeStructuredLikeString(value);
3365
3421
  const pattern = LIKE_WILDCARD_REGEX.test(normalized) ? normalized : `%${normalized}%`;
3366
3422
  const literalSegments = pattern.split(LIKE_WILDCARD_REGEX).map((segment) => segment.trim()).filter((segment) => segment.length > 0);
3367
3423
  const tokens = [];
3368
3424
  const seen = /* @__PURE__ */ new Set();
3369
3425
  for (const segment of literalSegments) {
3370
- for (const token of toNgrams(segment)) {
3426
+ for (const token of toNgrams(segment, config)) {
3371
3427
  const containsToken = toContainsToken(token);
3372
3428
  if (!seen.has(containsToken)) {
3373
3429
  seen.add(containsToken);
3374
3430
  tokens.push(containsToken);
3375
- if (tokens.length >= MAX_TOKENS_PER_VALUE) {
3431
+ if (tokens.length >= config.maxTokensPerValue) {
3376
3432
  return tokens;
3377
3433
  }
3378
3434
  }
@@ -3505,6 +3561,9 @@ function addPosting(index, field, value, docId) {
3505
3561
  index.set(field, fieldIndex);
3506
3562
  }
3507
3563
  var StructuredInMemoryIndex = class {
3564
+ constructor(tokenizer) {
3565
+ this.tokenizer = tokenizer;
3566
+ }
3508
3567
  eqIndex = /* @__PURE__ */ new Map();
3509
3568
  containsIndex = /* @__PURE__ */ new Map();
3510
3569
  rangeIndex = /* @__PURE__ */ new Map();
@@ -3525,7 +3584,10 @@ var StructuredInMemoryIndex = class {
3525
3584
  }
3526
3585
  addPosting(this.eqIndex, field, value, docId);
3527
3586
  if (typeof value === "string") {
3528
- for (const token of buildStructuredStringContainsTokens(value)) {
3587
+ for (const token of buildStructuredStringContainsTokens(
3588
+ value,
3589
+ this.tokenizer
3590
+ )) {
3529
3591
  addPosting(this.containsIndex, field, token, docId);
3530
3592
  }
3531
3593
  }
@@ -3612,10 +3674,17 @@ var normalizeFields = (fields) => {
3612
3674
  return normalized;
3613
3675
  };
3614
3676
  var StructuredInMemoryBackend = class {
3677
+ /**
3678
+ * @param tokenizer Optional tokenizer overrides for structured string contains behavior.
3679
+ */
3680
+ constructor(tokenizer) {
3681
+ this.tokenizer = tokenizer;
3682
+ this.index = new StructuredInMemoryIndex(tokenizer);
3683
+ }
3615
3684
  docFields = /* @__PURE__ */ new Map();
3616
- index = new StructuredInMemoryIndex();
3685
+ index;
3617
3686
  rebuildIndex() {
3618
- const nextIndex = new StructuredInMemoryIndex();
3687
+ const nextIndex = new StructuredInMemoryIndex(this.tokenizer);
3619
3688
  for (const [docId, fields] of this.docFields.entries()) {
3620
3689
  nextIndex.addDocument(docId, fields);
3621
3690
  }
@@ -3705,7 +3774,8 @@ var structuredRangeIndexSchema = {
3705
3774
  };
3706
3775
  var structuredDocFieldsSchema = {
3707
3776
  partitionKey: "docId",
3708
- fieldsAttribute: "fields"
3777
+ fieldsAttribute: "fields",
3778
+ versionAttribute: "version"
3709
3779
  };
3710
3780
  function serializeStructuredValue(value) {
3711
3781
  if (value === null) {
@@ -3745,8 +3815,8 @@ function buildStructuredRangeItem(field, value, docId) {
3745
3815
  docId
3746
3816
  };
3747
3817
  }
3748
- function buildStructuredDocFieldsItem(docId, fields) {
3749
- return { docId, fields };
3818
+ function buildStructuredDocFieldsItem(docId, fields, version) {
3819
+ return { docId, fields, version };
3750
3820
  }
3751
3821
 
3752
3822
  // src/api/Indexing/structured/StructuredWriter.ts
@@ -3762,7 +3832,7 @@ function normalizeFields2(fields) {
3762
3832
  }
3763
3833
  return normalized;
3764
3834
  }
3765
- function buildTermEntries(docId, fields) {
3835
+ function buildTermEntries(docId, fields, options) {
3766
3836
  const entries = [];
3767
3837
  for (const [field, value] of Object.entries(fields)) {
3768
3838
  if (Array.isArray(value)) {
@@ -3773,7 +3843,10 @@ function buildTermEntries(docId, fields) {
3773
3843
  } else {
3774
3844
  entries.push(buildStructuredTermItem(field, value, "eq", docId));
3775
3845
  if (typeof value === "string") {
3776
- for (const token of buildStructuredStringContainsTokens(value)) {
3846
+ for (const token of buildStructuredStringContainsTokens(
3847
+ value,
3848
+ options.tokenizer
3849
+ )) {
3777
3850
  entries.push(buildStructuredTermItem(field, token, "contains", docId));
3778
3851
  }
3779
3852
  }
@@ -3830,9 +3903,11 @@ var StructuredDdbWriter = class {
3830
3903
  /**
3831
3904
  * @param dependencies Writer dependencies for persistence.
3832
3905
  */
3833
- constructor(dependencies3) {
3906
+ constructor(dependencies3, options = {}) {
3834
3907
  this.dependencies = dependencies3;
3908
+ this.options = options;
3835
3909
  }
3910
+ options;
3836
3911
  /**
3837
3912
  * Write structured fields for a document, diffing term/range entries.
3838
3913
  * @param docId Document id to write.
@@ -3841,29 +3916,52 @@ var StructuredDdbWriter = class {
3841
3916
  */
3842
3917
  async write(docId, fields) {
3843
3918
  const normalized = normalizeFields2(fields);
3844
- const previousFields = await this.dependencies.loadDocFields(docId);
3845
- const previousNormalized = previousFields ? normalizeFields2(previousFields) : {};
3846
- const previousTerms = buildTermEntries(docId, previousNormalized);
3847
- const nextTerms = buildTermEntries(docId, normalized);
3848
- const previousRanges = buildRangeEntries(docId, previousNormalized);
3849
- const nextRanges = buildRangeEntries(docId, normalized);
3850
- const termDiff = diffEntries(previousTerms, nextTerms, termEntryKey);
3851
- const rangeDiff = diffEntries(previousRanges, nextRanges, rangeEntryKey);
3852
- if (termDiff.toDelete.length > 0) {
3853
- await this.dependencies.deleteTermEntries(toTermKeys(termDiff.toDelete));
3854
- }
3855
- if (rangeDiff.toDelete.length > 0) {
3856
- await this.dependencies.deleteRangeEntries(
3857
- toRangeKeys(rangeDiff.toDelete)
3919
+ const maxRetries = this.options.maxConcurrentWriteRetries ?? 8;
3920
+ let attempts = 0;
3921
+ while (attempts <= maxRetries) {
3922
+ const previousState = await this.dependencies.loadDocFieldsState(docId);
3923
+ const previousNormalized = previousState ? normalizeFields2(previousState.fields) : {};
3924
+ const expectedVersion = previousState?.version;
3925
+ const previousTerms = buildTermEntries(
3926
+ docId,
3927
+ previousNormalized,
3928
+ this.options
3858
3929
  );
3930
+ const nextTerms = buildTermEntries(docId, normalized, this.options);
3931
+ const previousRanges = buildRangeEntries(docId, previousNormalized);
3932
+ const nextRanges = buildRangeEntries(docId, normalized);
3933
+ const termDiff = diffEntries(previousTerms, nextTerms, termEntryKey);
3934
+ const rangeDiff = diffEntries(previousRanges, nextRanges, rangeEntryKey);
3935
+ const noDiff = termDiff.toAdd.length === 0 && termDiff.toDelete.length === 0 && rangeDiff.toAdd.length === 0 && rangeDiff.toDelete.length === 0;
3936
+ if (noDiff) {
3937
+ return;
3938
+ }
3939
+ const swapped = await this.dependencies.putDocFieldsIfVersion(
3940
+ docId,
3941
+ expectedVersion,
3942
+ normalized
3943
+ );
3944
+ if (!swapped) {
3945
+ attempts += 1;
3946
+ continue;
3947
+ }
3948
+ if (termDiff.toDelete.length > 0) {
3949
+ await this.dependencies.deleteTermEntries(toTermKeys(termDiff.toDelete));
3950
+ }
3951
+ if (rangeDiff.toDelete.length > 0) {
3952
+ await this.dependencies.deleteRangeEntries(
3953
+ toRangeKeys(rangeDiff.toDelete)
3954
+ );
3955
+ }
3956
+ if (termDiff.toAdd.length > 0) {
3957
+ await this.dependencies.putTermEntries(termDiff.toAdd);
3958
+ }
3959
+ if (rangeDiff.toAdd.length > 0) {
3960
+ await this.dependencies.putRangeEntries(rangeDiff.toAdd);
3961
+ }
3962
+ return;
3859
3963
  }
3860
- if (termDiff.toAdd.length > 0) {
3861
- await this.dependencies.putTermEntries(termDiff.toAdd);
3862
- }
3863
- if (rangeDiff.toAdd.length > 0) {
3864
- await this.dependencies.putRangeEntries(rangeDiff.toAdd);
3865
- }
3866
- await this.dependencies.putDocFields(docId, normalized);
3964
+ throw new Error("Structured writer concurrent write retries exceeded.");
3867
3965
  }
3868
3966
  };
3869
3967
 
@@ -4052,7 +4150,7 @@ var StructuredDdbWriterDependencies = class {
4052
4150
  this.rangeTableName = config.tables.rangeIndex;
4053
4151
  this.docFieldsTableName = config.tables.docFields;
4054
4152
  }
4055
- async loadDocFields(docId) {
4153
+ async loadDocFieldsState(docId) {
4056
4154
  const response = await this.client.getItem({
4057
4155
  TableName: this.docFieldsTableName,
4058
4156
  Key: { [structuredDocFieldsSchema.partitionKey]: docId }
@@ -4061,22 +4159,40 @@ var StructuredDdbWriterDependencies = class {
4061
4159
  return void 0;
4062
4160
  }
4063
4161
  const item = response.Item;
4064
- return item.fields;
4162
+ if (!item.fields) {
4163
+ return void 0;
4164
+ }
4165
+ return {
4166
+ fields: item.fields,
4167
+ version: typeof item.version === "number" && Number.isFinite(item.version) ? item.version : 0
4168
+ };
4065
4169
  }
4066
- async putDocFields(docId, fields) {
4067
- const requestItems = {
4068
- [this.docFieldsTableName]: [
4069
- {
4070
- PutRequest: {
4071
- Item: {
4072
- [structuredDocFieldsSchema.partitionKey]: docId,
4073
- [structuredDocFieldsSchema.fieldsAttribute]: fields
4074
- }
4075
- }
4170
+ async putDocFieldsIfVersion(docId, expectedVersion, fields) {
4171
+ if (typeof expectedVersion === "undefined") {
4172
+ const createResult = await this.client.putItem({
4173
+ TableName: this.docFieldsTableName,
4174
+ Item: buildStructuredDocFieldsItem(docId, fields, 1),
4175
+ ConditionExpression: "attribute_not_exists(#docId)",
4176
+ ExpressionAttributeNames: {
4177
+ "#docId": structuredDocFieldsSchema.partitionKey
4076
4178
  }
4077
- ]
4078
- };
4079
- await batchWriteWithRetry(this.client, requestItems);
4179
+ });
4180
+ return !createResult.conditionFailed;
4181
+ }
4182
+ const nextVersion = expectedVersion + 1;
4183
+ const updateResult = await this.client.putItem({
4184
+ TableName: this.docFieldsTableName,
4185
+ Item: buildStructuredDocFieldsItem(docId, fields, nextVersion),
4186
+ ConditionExpression: "(#version = :expectedVersion) OR (attribute_not_exists(#version) AND :expectedVersion = :zero)",
4187
+ ExpressionAttributeNames: {
4188
+ "#version": structuredDocFieldsSchema.versionAttribute
4189
+ },
4190
+ ExpressionAttributeValues: {
4191
+ ":expectedVersion": expectedVersion,
4192
+ ":zero": 0
4193
+ }
4194
+ });
4195
+ return !updateResult.conditionFailed;
4080
4196
  }
4081
4197
  async putTermEntries(entries) {
4082
4198
  await this.batchWrite(
@@ -4154,7 +4270,11 @@ var StructuredDdbBackend = class {
4154
4270
  constructor(config) {
4155
4271
  this.reader = new StructuredDdbReader(config);
4156
4272
  this.writer = new StructuredDdbWriter(
4157
- new StructuredDdbWriterDependencies(config)
4273
+ new StructuredDdbWriterDependencies(config),
4274
+ {
4275
+ ...config.writerOptions,
4276
+ tokenizer: config.writerOptions?.tokenizer ?? config.tokenizer
4277
+ }
4158
4278
  );
4159
4279
  }
4160
4280
  };
@@ -4354,7 +4474,7 @@ var compareArray = (fieldCriterion, fieldValue) => {
4354
4474
  }
4355
4475
  };
4356
4476
  var getFilterTypeInfoDataItemsBySearchCriteria = (searchCriteria, items, typeInfoName, typeInfoMap) => {
4357
- const { fields = {} } = {};
4477
+ const { fields = {} } = typeInfoMap?.[typeInfoName] || {};
4358
4478
  const { logicalOperator = "AND" /* AND */, fieldCriteria = [] } = searchCriteria;
4359
4479
  const filteredItems = [];
4360
4480
  for (const currentItem of items) {
@@ -7006,7 +7126,7 @@ var buildTerm = (fieldName, mode, value) => ({
7006
7126
  mode,
7007
7127
  value
7008
7128
  });
7009
- var buildWhereForCriterion = (criterion) => {
7129
+ var buildWhereForCriterion = (criterion, tokenizer) => {
7010
7130
  const { fieldName, operator = "EQUALS" /* EQUALS */, value } = criterion;
7011
7131
  switch (operator) {
7012
7132
  case "EQUALS" /* EQUALS */:
@@ -7017,7 +7137,7 @@ var buildWhereForCriterion = (criterion) => {
7017
7137
  if (typeof value !== "string") {
7018
7138
  return buildTerm(fieldName, "contains", value);
7019
7139
  }
7020
- const tokens = buildStructuredLikePatternTokens(value);
7140
+ const tokens = buildStructuredLikePatternTokens(value, tokenizer);
7021
7141
  const tokenClauses = tokens.map(
7022
7142
  (token) => buildTerm(fieldName, "contains", token)
7023
7143
  );
@@ -7063,12 +7183,12 @@ var buildWhereForCriterion = (criterion) => {
7063
7183
  };
7064
7184
  }
7065
7185
  };
7066
- var criteriaToStructuredWhere = (criteria) => {
7186
+ var criteriaToStructuredWhere = (criteria, tokenizer) => {
7067
7187
  if (!criteria || !Array.isArray(criteria.fieldCriteria)) {
7068
7188
  return void 0;
7069
7189
  }
7070
7190
  const clauses = criteria.fieldCriteria.map(
7071
- (criterion) => buildWhereForCriterion(criterion)
7191
+ (criterion) => buildWhereForCriterion(criterion, tokenizer)
7072
7192
  );
7073
7193
  if (clauses.length === 0) {
7074
7194
  return void 0;
@@ -7115,6 +7235,37 @@ var TypeInfoORMService = class {
7115
7235
  }
7116
7236
  dacRoleCache = {};
7117
7237
  indexingRelationshipDriver;
7238
+ /**
7239
+ * Emit list routing decision observability events without impacting runtime behavior.
7240
+ */
7241
+ emitListRoutingDecision = (typeName, path, reason, criteriaCount) => {
7242
+ const hook = this.config.indexing?.observability?.onListRoutingDecision;
7243
+ if (!hook) {
7244
+ return;
7245
+ }
7246
+ try {
7247
+ hook({
7248
+ typeName,
7249
+ path,
7250
+ reason,
7251
+ criteriaCount
7252
+ });
7253
+ } catch (_error) {
7254
+ }
7255
+ };
7256
+ /**
7257
+ * Emit structured index write observability events without impacting behavior.
7258
+ */
7259
+ emitStructuredIndexWrite = (typeName, docId, action, indexedFieldCount) => {
7260
+ const hook = this.config.indexing?.observability?.onStructuredIndexWrite;
7261
+ if (!hook) {
7262
+ return;
7263
+ }
7264
+ try {
7265
+ hook({ typeName, docId, action, indexedFieldCount });
7266
+ } catch (_error) {
7267
+ }
7268
+ };
7118
7269
  resolveAccessingRole = async (context) => {
7119
7270
  const { useDAC } = this.config;
7120
7271
  if (!useDAC) {
@@ -7374,14 +7525,278 @@ var TypeInfoORMService = class {
7374
7525
  return typeInfo;
7375
7526
  };
7376
7527
  /**
7377
- * @returns Resolved index field name, if available.
7528
+ * @returns Resolved full-text index field names.
7378
7529
  */
7379
- resolveFullTextIndexField = (typeName, override) => {
7530
+ resolveFullTextIndexFields = (typeName, override) => {
7380
7531
  if (override) {
7381
- return override;
7532
+ return [override];
7533
+ }
7534
+ const defaults = this.config.indexing?.fullText?.defaultIndexFieldByType?.[typeName];
7535
+ if (typeof defaults === "string") {
7536
+ return [defaults];
7537
+ }
7538
+ if (!Array.isArray(defaults)) {
7539
+ return [];
7540
+ }
7541
+ const seen = /* @__PURE__ */ new Set();
7542
+ const fields = [];
7543
+ for (const field of defaults) {
7544
+ if (typeof field !== "string") {
7545
+ continue;
7546
+ }
7547
+ const trimmed = field.trim();
7548
+ if (!trimmed || seen.has(trimmed)) {
7549
+ continue;
7550
+ }
7551
+ seen.add(trimmed);
7552
+ fields.push(trimmed);
7553
+ }
7554
+ return fields;
7555
+ };
7556
+ /**
7557
+ * @returns True when the operator maps to full-text search.
7558
+ */
7559
+ isFullTextSearchOperator = (operator) => operator === "LIKE" /* LIKE */ || operator === "CONTAINS" /* CONTAINS */ || operator === "STARTS_WITH" /* STARTS_WITH */;
7560
+ /**
7561
+ * @returns Explicitly indexed structured field names for a type.
7562
+ */
7563
+ resolveStructuredIndexedFields = (typeName) => {
7564
+ const configured = this.config.indexing?.structured?.indexedFieldsByType?.[typeName];
7565
+ if (!Array.isArray(configured)) {
7566
+ return /* @__PURE__ */ new Set();
7567
+ }
7568
+ const fields = /* @__PURE__ */ new Set();
7569
+ for (const field of configured) {
7570
+ if (typeof field !== "string") {
7571
+ continue;
7572
+ }
7573
+ const trimmed = field.trim();
7574
+ if (trimmed) {
7575
+ fields.add(trimmed);
7576
+ }
7577
+ }
7578
+ return fields;
7579
+ };
7580
+ /**
7581
+ * @returns True when the field type and operator can be served by structured indexing.
7582
+ */
7583
+ isStructuredOperatorSupportedForField = (field, operator) => {
7584
+ const { array: isArray = false, type } = field;
7585
+ if (isArray) {
7586
+ return operator === "CONTAINS" /* CONTAINS */;
7587
+ }
7588
+ if (type === "string") {
7589
+ return operator === "EQUALS" /* EQUALS */ || operator === "IN" /* IN */ || operator === "LIKE" /* LIKE */ || operator === "GREATER_THAN_OR_EQUAL" /* GREATER_THAN_OR_EQUAL */ || operator === "LESS_THAN_OR_EQUAL" /* LESS_THAN_OR_EQUAL */ || operator === "BETWEEN" /* BETWEEN */;
7590
+ }
7591
+ if (type === "number") {
7592
+ return operator === "EQUALS" /* EQUALS */ || operator === "IN" /* IN */ || operator === "GREATER_THAN_OR_EQUAL" /* GREATER_THAN_OR_EQUAL */ || operator === "LESS_THAN_OR_EQUAL" /* LESS_THAN_OR_EQUAL */ || operator === "BETWEEN" /* BETWEEN */;
7593
+ }
7594
+ if (type === "boolean") {
7595
+ return operator === "EQUALS" /* EQUALS */ || operator === "IN" /* IN */;
7596
+ }
7597
+ return false;
7598
+ };
7599
+ /**
7600
+ * @returns True when criteria can be evaluated using structured indexing.
7601
+ */
7602
+ canUseStructuredIndexForCriteria = (typeName, criteria) => {
7603
+ if (!criteria?.fieldCriteria?.length) {
7604
+ return false;
7605
+ }
7606
+ const typeInfo = this.getTypeInfo(typeName);
7607
+ const { fields = {} } = typeInfo;
7608
+ const indexedFields = this.resolveStructuredIndexedFields(typeName);
7609
+ if (indexedFields.size === 0) {
7610
+ return false;
7611
+ }
7612
+ for (const criterion of criteria.fieldCriteria) {
7613
+ const { fieldName } = criterion;
7614
+ const field = fields[fieldName];
7615
+ if (!field || field.typeReference || !indexedFields.has(fieldName)) {
7616
+ return false;
7617
+ }
7618
+ const operator = criterion.operator ?? "EQUALS" /* EQUALS */;
7619
+ if (!this.isStructuredOperatorSupportedForField(field, operator)) {
7620
+ return false;
7621
+ }
7622
+ }
7623
+ return true;
7624
+ };
7625
+ /**
7626
+ * @returns Full-text query plan derived from a field criterion.
7627
+ */
7628
+ toFullTextSearchPlan = (criterion) => {
7629
+ const operator = criterion.operator ?? "EQUALS" /* EQUALS */;
7630
+ if (!this.isFullTextSearchOperator(operator)) {
7631
+ return void 0;
7632
+ }
7633
+ if (typeof criterion.value !== "string") {
7634
+ throw {
7635
+ message: "INDEXING_UNSUPPORTED_CRITERIA" /* INDEXING_UNSUPPORTED_CRITERIA */,
7636
+ operator,
7637
+ fieldName: criterion.fieldName
7638
+ };
7639
+ }
7640
+ const query = criterion.value.trim();
7641
+ if (!query) {
7642
+ throw {
7643
+ message: "INDEXING_UNSUPPORTED_CRITERIA" /* INDEXING_UNSUPPORTED_CRITERIA */,
7644
+ operator,
7645
+ fieldName: criterion.fieldName
7646
+ };
7647
+ }
7648
+ if (operator === "CONTAINS" /* CONTAINS */) {
7649
+ return {
7650
+ mode: "exact",
7651
+ query
7652
+ };
7653
+ }
7654
+ if (operator === "STARTS_WITH" /* STARTS_WITH */) {
7655
+ return {
7656
+ mode: "lossy",
7657
+ query: `${query}*`
7658
+ };
7659
+ }
7660
+ return {
7661
+ mode: "lossy",
7662
+ query
7663
+ };
7664
+ };
7665
+ /**
7666
+ * @returns Auto full-text search plan for list criteria, if applicable.
7667
+ */
7668
+ resolveAutoFullTextCriteriaPlan = (typeName, criteria) => {
7669
+ if (!criteria?.fieldCriteria?.length) {
7670
+ return void 0;
7671
+ }
7672
+ const configuredIndexFields = new Set(
7673
+ this.resolveFullTextIndexFields(typeName)
7674
+ );
7675
+ if (configuredIndexFields.size === 0) {
7676
+ return void 0;
7677
+ }
7678
+ const fullTextCandidates = [];
7679
+ let hasNonFullTextCriteria = false;
7680
+ for (const criterion of criteria.fieldCriteria) {
7681
+ const indexField = criterion.fieldName;
7682
+ if (!configuredIndexFields.has(indexField)) {
7683
+ hasNonFullTextCriteria = true;
7684
+ continue;
7685
+ }
7686
+ const plan = this.toFullTextSearchPlan(criterion);
7687
+ if (!plan) {
7688
+ hasNonFullTextCriteria = true;
7689
+ continue;
7690
+ }
7691
+ fullTextCandidates.push({
7692
+ ...plan,
7693
+ indexField
7694
+ });
7695
+ }
7696
+ if (fullTextCandidates.length === 0) {
7697
+ return void 0;
7698
+ }
7699
+ if (hasNonFullTextCriteria || fullTextCandidates.length > 1 || criteria.logicalOperator === "OR" /* OR */) {
7700
+ throw {
7701
+ message: "INDEXING_UNSUPPORTED_COMBINATION" /* INDEXING_UNSUPPORTED_COMBINATION */,
7702
+ typeName
7703
+ };
7704
+ }
7705
+ return fullTextCandidates[0];
7706
+ };
7707
+ /**
7708
+ * @returns Encoded cursor for full-scan compare pagination.
7709
+ */
7710
+ encodeFullScanCompareCursor = (offset) => JSON.stringify({ fullScanCompareOffset: offset });
7711
+ /**
7712
+ * @returns Decoded offset for full-scan compare pagination.
7713
+ */
7714
+ decodeFullScanCompareCursor = (cursor) => {
7715
+ if (!cursor) {
7716
+ return 0;
7717
+ }
7718
+ try {
7719
+ const parsed = JSON.parse(cursor);
7720
+ const offset = parsed.fullScanCompareOffset;
7721
+ if (!Number.isFinite(offset) || offset < 0) {
7722
+ throw new Error("Invalid full scan cursor offset");
7723
+ }
7724
+ return offset;
7725
+ } catch (_error) {
7726
+ throw {
7727
+ message: "INVALID_CURSOR" /* INVALID_CURSOR */,
7728
+ cursor
7729
+ };
7382
7730
  }
7383
- return this.config.indexing?.fullText?.defaultIndexFieldByType?.[typeName];
7384
7731
  };
7732
+ /**
7733
+ * Execute a criteria list via full scan + in-memory compare.
7734
+ *
7735
+ * This is the universal fallback strategy for criteria/operators that are not
7736
+ * supported by indexed query planners.
7737
+ *
7738
+ * @returns List results with cursor.
7739
+ */
7740
+ async listByFullScanAndCompare(typeName, config, cleanSelectedFields, useDAC, context) {
7741
+ const driver = this.getDriverInternal(typeName);
7742
+ const { criteria, sortFields, itemsPerPage = 10, cursor } = config;
7743
+ const allItems = [];
7744
+ let scanCursor;
7745
+ while (true) {
7746
+ const page = await driver.listItems({
7747
+ itemsPerPage: 250,
7748
+ cursor: scanCursor
7749
+ });
7750
+ allItems.push(...page.items ?? []);
7751
+ if (!page.cursor || page.cursor === scanCursor) {
7752
+ break;
7753
+ }
7754
+ scanCursor = page.cursor;
7755
+ }
7756
+ const filtered = criteria ? getFilterTypeInfoDataItemsBySearchCriteria(
7757
+ criteria,
7758
+ allItems,
7759
+ typeName,
7760
+ this.config.typeInfoMap
7761
+ ) : allItems;
7762
+ const sorted = getSortedItems(sortFields, filtered);
7763
+ let index = this.decodeFullScanCompareCursor(cursor);
7764
+ const cleanedItems = [];
7765
+ while (index < sorted.length && cleanedItems.length < itemsPerPage) {
7766
+ const item = sorted[index];
7767
+ index += 1;
7768
+ let fieldsResources;
7769
+ if (useDAC) {
7770
+ const {
7771
+ allowed: readAllowed,
7772
+ denied: readDenied,
7773
+ fieldsResources: nextFieldsResources = {}
7774
+ } = await this.getItemDACValidation(
7775
+ item,
7776
+ typeName,
7777
+ "READ" /* READ */,
7778
+ context
7779
+ );
7780
+ const listDenied = readDenied || !readAllowed;
7781
+ if (listDenied) {
7782
+ continue;
7783
+ }
7784
+ fieldsResources = nextFieldsResources;
7785
+ }
7786
+ cleanedItems.push(
7787
+ this.getCleanItem(
7788
+ typeName,
7789
+ item,
7790
+ fieldsResources,
7791
+ cleanSelectedFields
7792
+ )
7793
+ );
7794
+ }
7795
+ return {
7796
+ items: cleanedItems,
7797
+ cursor: index < sorted.length ? this.encodeFullScanCompareCursor(index) : void 0
7798
+ };
7799
+ }
7385
7800
  /**
7386
7801
  * @param value Value to check.
7387
7802
  * @returns True when the value is a supported structured value.
@@ -7393,9 +7808,13 @@ var TypeInfoORMService = class {
7393
7808
  buildStructuredFields = (typeName, item) => {
7394
7809
  const typeInfo = this.getTypeInfo(typeName);
7395
7810
  const fieldMap = this.config.indexing?.structured?.fieldMapByType?.[typeName];
7811
+ const indexedFields = this.resolveStructuredIndexedFields(typeName);
7396
7812
  const withoutRefs = removeTypeReferenceFieldsFromDataItem(typeInfo, item);
7397
7813
  const fields = {};
7398
7814
  for (const [fieldName, value] of Object.entries(withoutRefs)) {
7815
+ if (!indexedFields.has(fieldName)) {
7816
+ continue;
7817
+ }
7399
7818
  if (typeof value === "undefined") {
7400
7819
  continue;
7401
7820
  }
@@ -7444,88 +7863,94 @@ var TypeInfoORMService = class {
7444
7863
  */
7445
7864
  async indexFullTextDocument(typeName, item, indexFieldOverride) {
7446
7865
  const { fullText } = this.config.indexing ?? {};
7447
- const indexField = this.resolveFullTextIndexField(
7866
+ const indexFields = this.resolveFullTextIndexFields(
7448
7867
  typeName,
7449
7868
  indexFieldOverride
7450
7869
  );
7451
- if (!fullText || !indexField) {
7870
+ if (!fullText || indexFields.length === 0) {
7452
7871
  return;
7453
7872
  }
7454
- const qualifiedIndexField = qualifyIndexField(typeName, indexField);
7455
7873
  const { primaryField } = this.getTypeInfo(typeName);
7456
- if (!(indexField in item)) {
7457
- throw {
7458
- message: "INDEXING_MISSING_INDEX_FIELD" /* INDEXING_MISSING_INDEX_FIELD */,
7459
- typeName,
7460
- indexField
7461
- };
7874
+ for (const indexField of indexFields) {
7875
+ const qualifiedIndexField = qualifyIndexField(typeName, indexField);
7876
+ if (!(indexField in item)) {
7877
+ throw {
7878
+ message: "INDEXING_MISSING_INDEX_FIELD" /* INDEXING_MISSING_INDEX_FIELD */,
7879
+ typeName,
7880
+ indexField
7881
+ };
7882
+ }
7883
+ await indexDocument({
7884
+ backend: fullText.backend,
7885
+ document: item,
7886
+ primaryField: String(primaryField),
7887
+ indexField,
7888
+ indexFieldQualified: qualifiedIndexField
7889
+ });
7462
7890
  }
7463
- await indexDocument({
7464
- backend: fullText.backend,
7465
- document: item,
7466
- primaryField: String(primaryField),
7467
- indexField,
7468
- indexFieldQualified: qualifiedIndexField
7469
- });
7470
7891
  }
7471
7892
  /**
7472
7893
  * @returns Promise resolved once removal is complete.
7473
7894
  */
7474
7895
  async removeFullTextDocument(typeName, item, indexFieldOverride) {
7475
7896
  const { fullText } = this.config.indexing ?? {};
7476
- const indexField = this.resolveFullTextIndexField(
7897
+ const indexFields = this.resolveFullTextIndexFields(
7477
7898
  typeName,
7478
7899
  indexFieldOverride
7479
7900
  );
7480
- if (!fullText || !indexField) {
7901
+ if (!fullText || indexFields.length === 0) {
7481
7902
  return;
7482
7903
  }
7483
- const qualifiedIndexField = qualifyIndexField(typeName, indexField);
7484
7904
  const { primaryField } = this.getTypeInfo(typeName);
7485
- if (!(indexField in item)) {
7486
- throw {
7487
- message: "INDEXING_MISSING_INDEX_FIELD" /* INDEXING_MISSING_INDEX_FIELD */,
7488
- typeName,
7489
- indexField
7490
- };
7905
+ for (const indexField of indexFields) {
7906
+ const qualifiedIndexField = qualifyIndexField(typeName, indexField);
7907
+ if (!(indexField in item)) {
7908
+ throw {
7909
+ message: "INDEXING_MISSING_INDEX_FIELD" /* INDEXING_MISSING_INDEX_FIELD */,
7910
+ typeName,
7911
+ indexField
7912
+ };
7913
+ }
7914
+ await removeDocument({
7915
+ backend: fullText.backend,
7916
+ document: item,
7917
+ primaryField: String(primaryField),
7918
+ indexField,
7919
+ indexFieldQualified: qualifiedIndexField
7920
+ });
7491
7921
  }
7492
- await removeDocument({
7493
- backend: fullText.backend,
7494
- document: item,
7495
- primaryField: String(primaryField),
7496
- indexField,
7497
- indexFieldQualified: qualifiedIndexField
7498
- });
7499
7922
  }
7500
7923
  /**
7501
7924
  * @returns Promise resolved once replacement is complete.
7502
7925
  */
7503
7926
  async replaceFullTextDocument(typeName, previousItem, nextItem, indexFieldOverride) {
7504
7927
  const { fullText } = this.config.indexing ?? {};
7505
- const indexField = this.resolveFullTextIndexField(
7928
+ const indexFields = this.resolveFullTextIndexFields(
7506
7929
  typeName,
7507
7930
  indexFieldOverride
7508
7931
  );
7509
- if (!fullText || !indexField) {
7932
+ if (!fullText || indexFields.length === 0) {
7510
7933
  return;
7511
7934
  }
7512
- const qualifiedIndexField = qualifyIndexField(typeName, indexField);
7513
7935
  const { primaryField } = this.getTypeInfo(typeName);
7514
- if (!(indexField in previousItem)) {
7515
- throw {
7516
- message: "INDEXING_MISSING_INDEX_FIELD" /* INDEXING_MISSING_INDEX_FIELD */,
7517
- typeName,
7518
- indexField
7519
- };
7936
+ for (const indexField of indexFields) {
7937
+ const qualifiedIndexField = qualifyIndexField(typeName, indexField);
7938
+ if (!(indexField in previousItem)) {
7939
+ throw {
7940
+ message: "INDEXING_MISSING_INDEX_FIELD" /* INDEXING_MISSING_INDEX_FIELD */,
7941
+ typeName,
7942
+ indexField
7943
+ };
7944
+ }
7945
+ await replaceFullTextDocument({
7946
+ backend: fullText.backend,
7947
+ previousDocument: previousItem,
7948
+ nextDocument: nextItem,
7949
+ primaryField: String(primaryField),
7950
+ indexField,
7951
+ indexFieldQualified: qualifiedIndexField
7952
+ });
7520
7953
  }
7521
- await replaceFullTextDocument({
7522
- backend: fullText.backend,
7523
- previousDocument: previousItem,
7524
- nextDocument: nextItem,
7525
- primaryField: String(primaryField),
7526
- indexField,
7527
- indexFieldQualified: qualifiedIndexField
7528
- });
7529
7954
  }
7530
7955
  /**
7531
7956
  * @returns Promise resolved once indexing is complete.
@@ -7549,6 +7974,12 @@ var TypeInfoORMService = class {
7549
7974
  primaryFieldName
7550
7975
  );
7551
7976
  const fields = this.buildStructuredFields(typeName, item);
7977
+ this.emitStructuredIndexWrite(
7978
+ typeName,
7979
+ String(docId),
7980
+ "upsert",
7981
+ Object.keys(fields).length
7982
+ );
7552
7983
  await structured.writer.write(docId, fields);
7553
7984
  }
7554
7985
  /**
@@ -7572,6 +8003,7 @@ var TypeInfoORMService = class {
7572
8003
  item[primaryFieldName],
7573
8004
  primaryFieldName
7574
8005
  );
8006
+ this.emitStructuredIndexWrite(typeName, String(docId), "remove", 0);
7575
8007
  await structured.writer.write(docId, {});
7576
8008
  }
7577
8009
  /**
@@ -8249,7 +8681,7 @@ var TypeInfoORMService = class {
8249
8681
  this.validateReadOperation(typeName, cleanSelectedFields);
8250
8682
  const { typeInfoMap, useDAC, indexing } = this.config;
8251
8683
  this.getTypeInfo(typeName);
8252
- const { criteria, text, itemsPerPage, cursor, sortFields } = config;
8684
+ const { criteria, itemsPerPage, cursor, sortFields } = config;
8253
8685
  const { fieldCriteria = [] } = criteria || {};
8254
8686
  const searchFieldValidationResults = validateSearchFields(
8255
8687
  typeName,
@@ -8260,55 +8692,36 @@ var TypeInfoORMService = class {
8260
8692
  const hasStructured = !!indexing?.structured?.reader;
8261
8693
  const hasFullText = !!indexing?.fullText?.backend;
8262
8694
  const hasCriteria = !!criteria && fieldCriteria.length > 0;
8263
- const hasText = !!text;
8264
- const shouldUseIndexing = !!hasCriteria || !!hasText;
8265
- if (hasStructured || hasFullText) {
8266
- if (!shouldUseIndexing) ; else {
8267
- if (hasCriteria && hasText) {
8268
- throw {
8269
- message: "INDEXING_UNSUPPORTED_COMBINATION" /* INDEXING_UNSUPPORTED_COMBINATION */,
8270
- typeName
8271
- };
8272
- }
8273
- if (hasCriteria && !hasStructured) {
8274
- throw {
8275
- message: "INDEXING_MISSING_BACKEND" /* INDEXING_MISSING_BACKEND */,
8276
- typeName,
8277
- backend: "structured.reader"
8278
- };
8279
- }
8280
- if (hasText && !hasFullText) {
8281
- throw {
8282
- message: "INDEXING_MISSING_BACKEND" /* INDEXING_MISSING_BACKEND */,
8283
- typeName,
8284
- backend: "fullText"
8285
- };
8286
- }
8695
+ if (hasCriteria && (hasStructured || hasFullText)) {
8696
+ try {
8287
8697
  let docIds = [];
8288
8698
  let nextCursor = void 0;
8289
- if (hasText) {
8290
- const indexField = this.resolveFullTextIndexField(
8699
+ const fullTextPlan = this.resolveAutoFullTextCriteriaPlan(
8700
+ typeName,
8701
+ criteria
8702
+ );
8703
+ if (fullTextPlan && hasFullText) {
8704
+ this.emitListRoutingDecision(
8291
8705
  typeName,
8292
- text?.indexField
8706
+ "fullText",
8707
+ "fullTextPlan",
8708
+ fieldCriteria.length
8709
+ );
8710
+ const qualifiedIndexField = qualifyIndexField(
8711
+ typeName,
8712
+ fullTextPlan.indexField
8293
8713
  );
8294
- if (!indexField) {
8295
- throw {
8296
- message: "INDEXING_MISSING_INDEX_FIELD" /* INDEXING_MISSING_INDEX_FIELD */,
8297
- typeName
8298
- };
8299
- }
8300
- const qualifiedIndexField = qualifyIndexField(typeName, indexField);
8301
8714
  const fullTextBackend = indexing?.fullText?.backend;
8302
- const searchResult = text?.mode === "exact" ? await searchExact({
8715
+ const searchResult = fullTextPlan.mode === "exact" ? await searchExact({
8303
8716
  backend: fullTextBackend,
8304
- query: text.query,
8717
+ query: fullTextPlan.query,
8305
8718
  indexField: qualifiedIndexField,
8306
8719
  limit: itemsPerPage,
8307
8720
  cursor,
8308
8721
  limits: indexing?.limits
8309
8722
  }) : await searchLossy({
8310
8723
  backend: fullTextBackend,
8311
- query: text?.query ?? "",
8724
+ query: fullTextPlan.query,
8312
8725
  indexField: qualifiedIndexField,
8313
8726
  limit: itemsPerPage,
8314
8727
  cursor,
@@ -8316,9 +8729,25 @@ var TypeInfoORMService = class {
8316
8729
  });
8317
8730
  docIds = searchResult.docIds;
8318
8731
  nextCursor = searchResult.nextCursor;
8319
- } else {
8320
- const where = criteriaToStructuredWhere(criteria);
8321
- if (!where) {
8732
+ } else if (hasStructured) {
8733
+ if (!this.canUseStructuredIndexForCriteria(typeName, criteria)) {
8734
+ throw {
8735
+ message: "INDEXING_UNSUPPORTED_CRITERIA" /* INDEXING_UNSUPPORTED_CRITERIA */,
8736
+ typeName
8737
+ };
8738
+ }
8739
+ this.emitListRoutingDecision(
8740
+ typeName,
8741
+ "structured",
8742
+ "structuredEligible",
8743
+ fieldCriteria.length
8744
+ );
8745
+ const tokenizer = indexing?.structured?.tokenizer;
8746
+ const whereWithTokenizer = criteriaToStructuredWhere(
8747
+ criteria,
8748
+ tokenizer
8749
+ );
8750
+ if (!whereWithTokenizer) {
8322
8751
  throw {
8323
8752
  message: "INDEXING_UNSUPPORTED_CRITERIA" /* INDEXING_UNSUPPORTED_CRITERIA */,
8324
8753
  typeName
@@ -8326,7 +8755,7 @@ var TypeInfoORMService = class {
8326
8755
  }
8327
8756
  const mappedWhere = this.applyStructuredFieldMap(
8328
8757
  typeName,
8329
- where,
8758
+ whereWithTokenizer,
8330
8759
  indexing?.structured?.fieldMapByType?.[typeName]
8331
8760
  );
8332
8761
  const structuredReader = indexing?.structured?.reader;
@@ -8340,6 +8769,11 @@ var TypeInfoORMService = class {
8340
8769
  );
8341
8770
  docIds = page.candidateIds;
8342
8771
  nextCursor = page.cursor;
8772
+ } else {
8773
+ throw {
8774
+ message: "INDEXING_UNSUPPORTED_CRITERIA" /* INDEXING_UNSUPPORTED_CRITERIA */,
8775
+ typeName
8776
+ };
8343
8777
  }
8344
8778
  const driver2 = this.getDriverInternal(typeName);
8345
8779
  const items = [];
@@ -8392,8 +8826,37 @@ var TypeInfoORMService = class {
8392
8826
  items: sortedItems,
8393
8827
  cursor: nextCursor
8394
8828
  };
8829
+ } catch (_error) {
8830
+ this.emitListRoutingDecision(
8831
+ typeName,
8832
+ "fullScanCompare",
8833
+ "indexedPathFailedOrUnsupported",
8834
+ fieldCriteria.length
8835
+ );
8836
+ return this.listByFullScanAndCompare(
8837
+ typeName,
8838
+ config,
8839
+ cleanSelectedFields,
8840
+ useDAC,
8841
+ context
8842
+ );
8395
8843
  }
8396
8844
  }
8845
+ if (hasCriteria) {
8846
+ this.emitListRoutingDecision(
8847
+ typeName,
8848
+ "fullScanCompare",
8849
+ "criteriaWithoutIndexedPath",
8850
+ fieldCriteria.length
8851
+ );
8852
+ return this.listByFullScanAndCompare(
8853
+ typeName,
8854
+ config,
8855
+ cleanSelectedFields,
8856
+ useDAC,
8857
+ context
8858
+ );
8859
+ }
8397
8860
  const driver = this.getDriverInternal(typeName);
8398
8861
  const fieldsResourcesCache = [];
8399
8862
  const results = await executeDriverListItems(