@dereekb/util 13.12.3 → 13.12.5

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.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@dereekb/util/eslint",
3
- "version": "13.12.3",
3
+ "version": "13.12.5",
4
4
  "peerDependencies": {
5
- "@dereekb/util": "13.12.3",
5
+ "@dereekb/util": "13.12.5",
6
6
  "@typescript-eslint/utils": "8.59.3"
7
7
  },
8
8
  "devDependencies": {
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@dereekb/util/fetch",
3
- "version": "13.12.3",
3
+ "version": "13.12.5",
4
4
  "peerDependencies": {
5
- "@dereekb/util": "13.12.3",
5
+ "@dereekb/util": "13.12.5",
6
6
  "make-error": "^1.3.6",
7
7
  "fast-content-type-parse": "^3.0.0"
8
8
  },
package/index.cjs.js CHANGED
@@ -25209,6 +25209,275 @@ function _ts_generator(thisArg, body) {
25209
25209
  return result;
25210
25210
  }
25211
25211
 
25212
+ /**
25213
+ * Spec-file naming conventions for API test files.
25214
+ *
25215
+ * Downstream Firebase Functions API apps keep their tests under
25216
+ * `<apiDir>/src/app/function/<group>/`, with each spec file named so that
25217
+ * "what does this test, and where do I add the next one?" can be answered
25218
+ * from the filename alone. The canonical forms are:
25219
+ *
25220
+ * - `<group>.crud.spec.ts` — non-scenario tests of the CRUD function map for the group.
25221
+ * - `<group>.crud.<sub>[.<sub>...].spec.ts` — CRUD tests focused on a sub-area
25222
+ * (e.g. `job.crud.requirement.spec.ts`).
25223
+ * - `<group>.scenario.spec.ts` — generic multi-step scenario tests using fixture
25224
+ * chains.
25225
+ * - `<group>.scenario.<sub>[.<sub>...].spec.ts` — focused scenario sub-bucket
25226
+ * (e.g. `job.scenario.requirement.worker.state.spec.ts`).
25227
+ *
25228
+ * Drift forms (still parsed, but flagged with a rename suggestion):
25229
+ * - `<group>.<sub>.crud.spec.ts` — `crud` placed after a subgroup.
25230
+ * Suggestion: `<group>.crud.<sub>.spec.ts`.
25231
+ * - `<group>.<sub>.scenario.spec.ts` — `scenario` placed after a subgroup
25232
+ * (e.g. `worker.payroll.scenario.spec.ts`, `school.job.publish.scenario.spec.ts`).
25233
+ * Suggestion: `<group>.scenario.<sub>.spec.ts`.
25234
+ * - `<group>.<rest>.spec.ts` — no `crud` / `scenario` segment at all
25235
+ * (e.g. `worker.system.spec.ts`). Defaults to a scenario rename suggestion
25236
+ * because the missing bucket is more often scenario-like.
25237
+ *
25238
+ * This module is pure: classification and canonical-path rendering live
25239
+ * here, with no disk I/O. It's consumed by `@dereekb/dbx-components-mcp`
25240
+ * (the `dbx_model_test_*` tool cluster) and by
25241
+ * `@dereekb/firebase/eslint` (the `require-canonical-api-spec-filename`
25242
+ * and `require-api-crud-spec-for-group` rules).
25243
+ */ /**
25244
+ * Whether a given kind is on-convention. Drift kinds (`-misplaced`,
25245
+ * `no-bucket`) are still parseable but earn a warning + rename suggestion.
25246
+ */ var CANONICAL_KINDS = [
25247
+ 'crud',
25248
+ 'crud-subgroup',
25249
+ 'scenario',
25250
+ 'scenario-subgroup'
25251
+ ];
25252
+ var SPEC_SUFFIX = '.spec.ts';
25253
+ /**
25254
+ * Classifies a spec filename against the conventions for the given parent
25255
+ * folder. Pure function — never touches the filesystem.
25256
+ *
25257
+ * @param config - Inputs.
25258
+ * @param config.filename - Bare filename including the `.spec.ts` suffix
25259
+ * (e.g. `job.scenario.requirement.spec.ts`).
25260
+ * @param config.parentFolderName - Name of the directory containing the file
25261
+ * (e.g. `job`). Used to detect cross-group misplacement.
25262
+ * @returns The classification.
25263
+ */ function classifySpecFile(config) {
25264
+ var filename = config.filename, parentFolderName = config.parentFolderName;
25265
+ var result;
25266
+ if (!filename.endsWith(SPEC_SUFFIX)) {
25267
+ result = {
25268
+ filename: filename,
25269
+ group: '',
25270
+ kind: 'non-spec',
25271
+ subgroups: [],
25272
+ isCanonical: false
25273
+ };
25274
+ } else {
25275
+ var _parts_;
25276
+ var stem = filename.slice(0, -SPEC_SUFFIX.length);
25277
+ var parts = stem.split('.');
25278
+ var group = (_parts_ = parts[0]) !== null && _parts_ !== void 0 ? _parts_ : '';
25279
+ if (group !== parentFolderName) {
25280
+ result = {
25281
+ filename: filename,
25282
+ group: group,
25283
+ kind: 'non-group',
25284
+ subgroups: [],
25285
+ isCanonical: false
25286
+ };
25287
+ } else {
25288
+ var rest = parts.slice(1);
25289
+ result = classifyRemainingSegments({
25290
+ filename: filename,
25291
+ group: group,
25292
+ rest: rest
25293
+ });
25294
+ }
25295
+ }
25296
+ return result;
25297
+ }
25298
+ function classifyRemainingSegments(config) {
25299
+ var filename = config.filename, group = config.group, rest = config.rest;
25300
+ var result;
25301
+ var crudIdx = rest.indexOf('crud');
25302
+ var scenarioIdx = rest.indexOf('scenario');
25303
+ if (crudIdx === 0) {
25304
+ if (rest.length === 1) {
25305
+ result = {
25306
+ filename: filename,
25307
+ group: group,
25308
+ kind: 'crud',
25309
+ subgroups: [],
25310
+ isCanonical: true
25311
+ };
25312
+ } else {
25313
+ result = {
25314
+ filename: filename,
25315
+ group: group,
25316
+ kind: 'crud-subgroup',
25317
+ subgroups: rest.slice(1),
25318
+ isCanonical: true
25319
+ };
25320
+ }
25321
+ } else if (scenarioIdx === 0) {
25322
+ if (rest.length === 1) {
25323
+ result = {
25324
+ filename: filename,
25325
+ group: group,
25326
+ kind: 'scenario',
25327
+ subgroups: [],
25328
+ isCanonical: true
25329
+ };
25330
+ } else {
25331
+ result = {
25332
+ filename: filename,
25333
+ group: group,
25334
+ kind: 'scenario-subgroup',
25335
+ subgroups: rest.slice(1),
25336
+ isCanonical: true
25337
+ };
25338
+ }
25339
+ } else if (crudIdx > 0) {
25340
+ var subgroups = rest.filter(function(_, i) {
25341
+ return i !== crudIdx;
25342
+ });
25343
+ var recommendedRename = buildCanonicalFilename({
25344
+ group: group,
25345
+ bucket: 'crud',
25346
+ subgroups: subgroups
25347
+ });
25348
+ result = {
25349
+ filename: filename,
25350
+ group: group,
25351
+ kind: 'crud-misplaced',
25352
+ subgroups: subgroups,
25353
+ isCanonical: false,
25354
+ recommendedRename: recommendedRename,
25355
+ driftReason: '`crud` segment is not directly after the group name.'
25356
+ };
25357
+ } else if (scenarioIdx > 0) {
25358
+ var subgroups1 = rest.filter(function(_, i) {
25359
+ return i !== scenarioIdx;
25360
+ });
25361
+ var recommendedRename1 = buildCanonicalFilename({
25362
+ group: group,
25363
+ bucket: 'scenario',
25364
+ subgroups: subgroups1
25365
+ });
25366
+ result = {
25367
+ filename: filename,
25368
+ group: group,
25369
+ kind: 'scenario-misplaced',
25370
+ subgroups: subgroups1,
25371
+ isCanonical: false,
25372
+ recommendedRename: recommendedRename1,
25373
+ driftReason: '`scenario` segment is not directly after the group name.'
25374
+ };
25375
+ } else if (rest.length === 0) {
25376
+ var recommendedRename2 = buildCanonicalFilename({
25377
+ group: group,
25378
+ bucket: 'scenario',
25379
+ subgroups: []
25380
+ });
25381
+ result = {
25382
+ filename: filename,
25383
+ group: group,
25384
+ kind: 'no-bucket',
25385
+ subgroups: [],
25386
+ isCanonical: false,
25387
+ recommendedRename: recommendedRename2,
25388
+ driftReason: 'Missing `crud` or `scenario` segment.'
25389
+ };
25390
+ } else {
25391
+ var recommendedRename3 = buildCanonicalFilename({
25392
+ group: group,
25393
+ bucket: 'scenario',
25394
+ subgroups: rest
25395
+ });
25396
+ result = {
25397
+ filename: filename,
25398
+ group: group,
25399
+ kind: 'no-bucket',
25400
+ subgroups: rest,
25401
+ isCanonical: false,
25402
+ recommendedRename: recommendedRename3,
25403
+ driftReason: 'Missing `crud` or `scenario` segment — defaulting suggestion to `scenario`.'
25404
+ };
25405
+ }
25406
+ return result;
25407
+ }
25408
+ /**
25409
+ * Renders a canonical spec filename for the given group + bucket + subgroup
25410
+ * chain. Pure data — used both by drift remediation and by the
25411
+ * `recommendSpecPath()` helper below.
25412
+ *
25413
+ * @param config - Inputs.
25414
+ * @param config.group - The model-group name (e.g. `job`).
25415
+ * @param config.bucket - `crud` or `scenario`.
25416
+ * @param config.subgroups - Optional ordered subgroup segments
25417
+ * (e.g. `['requirement','worker']`).
25418
+ * @returns The canonical filename (e.g. `job.scenario.requirement.worker.spec.ts`).
25419
+ */ function buildCanonicalFilename(config) {
25420
+ var group = config.group, bucket = config.bucket, subgroups = config.subgroups;
25421
+ var tail = subgroups.length === 0 ? '' : ".".concat(subgroups.join('.'));
25422
+ return "".concat(group, ".").concat(bucket).concat(tail, ".spec.ts");
25423
+ }
25424
+ /**
25425
+ * Renders the canonical relative path for a spec file under a given API app.
25426
+ * The shape mirrors the layout used throughout the workspace:
25427
+ * `<apiDir>/src/app/function/<group>/<filename>`.
25428
+ *
25429
+ * @param config - Inputs.
25430
+ * @param config.apiDir - The relative API-app directory
25431
+ * (e.g. `apps/hellosubs-api`).
25432
+ * @param config.group - The model-group name.
25433
+ * @param config.bucket - `crud` or `scenario`.
25434
+ * @param config.subgroups - Optional subgroup chain.
25435
+ * @returns The canonical relative spec-file path.
25436
+ */ function recommendSpecPath(config) {
25437
+ var filename = buildCanonicalFilename({
25438
+ group: config.group,
25439
+ bucket: config.bucket,
25440
+ subgroups: config.subgroups
25441
+ });
25442
+ return "".concat(config.apiDir, "/src/app/function/").concat(config.group, "/").concat(filename);
25443
+ }
25444
+ /**
25445
+ * Returns the two canonical buckets for a given model group, with rendered
25446
+ * paths and one-line summaries. Used by the list-app formatter.
25447
+ *
25448
+ * @param config - Inputs.
25449
+ * @param config.apiDir - Relative API-app directory.
25450
+ * @param config.group - Model-group name.
25451
+ * @returns The crud + scenario recommendations.
25452
+ */ function recommendBucketsForGroup(config) {
25453
+ var crudPath = recommendSpecPath({
25454
+ apiDir: config.apiDir,
25455
+ group: config.group,
25456
+ bucket: 'crud',
25457
+ subgroups: []
25458
+ });
25459
+ var scenarioPath = recommendSpecPath({
25460
+ apiDir: config.apiDir,
25461
+ group: config.group,
25462
+ bucket: 'scenario',
25463
+ subgroups: []
25464
+ });
25465
+ return [
25466
+ {
25467
+ bucket: 'crud',
25468
+ label: 'CRUD',
25469
+ canonicalPath: crudPath,
25470
+ summary: 'Non-scenario tests of the CRUD function map — create/read/update/delete + permission/error paths. Add focused buckets as `' + config.group + '.crud.<sub>.spec.ts`.'
25471
+ },
25472
+ {
25473
+ bucket: 'scenario',
25474
+ label: 'Scenario',
25475
+ canonicalPath: scenarioPath,
25476
+ summary: 'Multi-step scenarios using fixture chains (mirrors real workflows). Add focused buckets as `' + config.group + '.scenario.<sub>.spec.ts` (chain subgroups freely, e.g. `.scenario.requirement.worker.spec.ts`).'
25477
+ }
25478
+ ];
25479
+ }
25480
+
25212
25481
  exports.ALL_DOUBLE_SLASHES_REGEX = ALL_DOUBLE_SLASHES_REGEX;
25213
25482
  exports.ALL_SLASHES_REGEX = ALL_SLASHES_REGEX;
25214
25483
  exports.ALL_SLASH_PATH_FILE_TYPE_SEPARATORS_REGEX = ALL_SLASH_PATH_FILE_TYPE_SEPARATORS_REGEX;
@@ -25228,6 +25497,7 @@ exports.AssertMin = AssertMin;
25228
25497
  exports.AssertionError = AssertionError;
25229
25498
  exports.AssertionIssueHandler = AssertionIssueHandler;
25230
25499
  exports.BooleanStringKeyArrayUtility = BooleanStringKeyArrayUtility;
25500
+ exports.CANONICAL_KINDS = CANONICAL_KINDS;
25231
25501
  exports.CATCH_ALL_HANDLE_RESULT_KEY = CATCH_ALL_HANDLE_RESULT_KEY;
25232
25502
  exports.COMMA_JOINER = COMMA_JOINER;
25233
25503
  exports.COMMA_STRING_SPLIT_JOIN = COMMA_STRING_SPLIT_JOIN;
@@ -25453,6 +25723,7 @@ exports.boundToRectangle = boundToRectangle;
25453
25723
  exports.breadthFirstExploreTreeTraversalFactoryFunction = breadthFirstExploreTreeTraversalFactoryFunction;
25454
25724
  exports.bufferHasValidPdfMarkings = bufferHasValidPdfMarkings;
25455
25725
  exports.build = build;
25726
+ exports.buildCanonicalFilename = buildCanonicalFilename;
25456
25727
  exports.cachedGetter = cachedGetter;
25457
25728
  exports.calculateExpirationDate = calculateExpirationDate;
25458
25729
  exports.camelOrPascalToScreamingSnake = camelOrPascalToScreamingSnake;
@@ -25466,6 +25737,7 @@ exports.chainMapSameFunctions = chainMapSameFunctions;
25466
25737
  exports.characterPrefixSuffixInstance = characterPrefixSuffixInstance;
25467
25738
  exports.checkAnyHaveExpired = checkAnyHaveExpired;
25468
25739
  exports.checkAtleastOneNotExpired = checkAtleastOneNotExpired;
25740
+ exports.classifySpecFile = classifySpecFile;
25469
25741
  exports.coerceToEmailParticipants = coerceToEmailParticipants;
25470
25742
  exports.combineMaps = combineMaps;
25471
25743
  exports.compareEqualityWithValueFromItemsFunction = compareEqualityWithValueFromItemsFunction;
@@ -26006,6 +26278,8 @@ exports.readableError = readableError;
26006
26278
  exports.readableStreamToBase64 = readableStreamToBase64;
26007
26279
  exports.readableStreamToBuffer = readableStreamToBuffer;
26008
26280
  exports.readableStreamToStringFunction = readableStreamToStringFunction;
26281
+ exports.recommendBucketsForGroup = recommendBucketsForGroup;
26282
+ exports.recommendSpecPath = recommendSpecPath;
26009
26283
  exports.rectangleOverlapsRectangle = rectangleOverlapsRectangle;
26010
26284
  exports.reduceBooleansFn = reduceBooleansFn;
26011
26285
  exports.reduceBooleansWithAnd = reduceBooleansWithAnd;
package/index.esm.js CHANGED
@@ -25207,4 +25207,273 @@ function _ts_generator(thisArg, body) {
25207
25207
  return result;
25208
25208
  }
25209
25209
 
25210
- export { ALL_DOUBLE_SLASHES_REGEX, ALL_SLASHES_REGEX, ALL_SLASH_PATH_FILE_TYPE_SEPARATORS_REGEX, ALL_TIME_UNITS, APPLICATION_FILE_EXTENSION_TO_MIME_TYPES_RECORD, APPLICATION_MIME_TYPES_TO_FILE_EXTENSIONS_RECORD, ASSERTION_ERROR_CODE, ASSERTION_HANDLER, AUTH_ADMIN_ROLE, AUTH_ONBOARDED_ROLE, AUTH_TOS_SIGNED_ROLE, AUTH_USER_ROLE, AbstractUniqueModel, Assert, AssertMax, AssertMin, AssertionError, AssertionIssueHandler, BooleanStringKeyArrayUtility, CATCH_ALL_HANDLE_RESULT_KEY, COMMA_JOINER, COMMA_STRING_SPLIT_JOIN, CSV_MIME_TYPE, CUT_VALUE_TO_ZERO_PRECISION, DASH_CHARACTER_PREFIX_INSTANCE, DATE_NOW_VALUE, DAYS_IN_WEEK, DAYS_IN_YEAR, DEFAULT_AUTH_ROLE_CLAIMS_CLAIM_VALUE, DEFAULT_AUTH_ROLE_CLAIMS_EMPTY_VALUE, DEFAULT_CUT_STRING_END_TEXT, DEFAULT_ENCRYPTED_FIELD_PREFIX, DEFAULT_LAT_LNG_STRING_VALUE, DEFAULT_NUMBER_STRING_DENCODER_64_NEGATIVE_PREFIX, DEFAULT_RANDOM_EMAIL_FACTORY_CONFIG, DEFAULT_RANDOM_PHONE_NUMBER_FACTORY_CONFIG, DEFAULT_READABLE_ERROR_CODE, DEFAULT_SLASH_PATH_ILLEGAL_CHARACTERS, DEFAULT_SLASH_PATH_ILLEGAL_CHARACTER_REPLACEMENT, DEFAULT_SLASH_PATH_PATH_MATCHER_NON_MATCHING_FILL_VALUE, DEFAULT_UNKNOWN_MODEL_TYPE_STRING, DOCUMENT_FILE_EXTENSION_TO_MIME_TYPES_RECORD, DOCUMENT_MIME_TYPES_TO_FILE_EXTENSIONS_RECORD, DOCX_MIME_TYPE, DOLLAR_AMOUNT_PRECISION, DOLLAR_AMOUNT_STRING_REGEX, DataDoesNotExistError, DataIsExpiredError, Day, DestroyFunctionObject, E164PHONE_NUMBER_REGEX, E164PHONE_NUMBER_WITH_EXTENSION_REGEX, E164PHONE_NUMBER_WITH_OPTIONAL_EXTENSION_REGEX, ExploreTreeVisitNodeDecision, FINAL_PAGE, FIRST_PAGE, FRACTIONAL_HOURS_PRECISION_FUNCTION, FlattenTreeAddNodeDecision, FullStorageObject, GIF_MIME_TYPE, HAS_PORT_NUMBER_REGEX, HAS_WEBSITE_DOMAIN_NAME_REGEX, HEIF_MIME_TYPE, HEX_PATTERN, HOURS_IN_DAY, HOUR_OF_DAY_MAXMIMUM, HOUR_OF_DAY_MINIUMUM, HTML_MIME_TYPE, HTTP_OR_HTTPS_REGEX, HashSet, IMAGE_FILE_EXTENSION_TO_MIME_TYPES_RECORD, IMAGE_MIME_TYPES_TO_FILE_EXTENSIONS_RECORD, ISO8601_DAY_STRING_REGEX, ISO8601_DAY_STRING_START_REGEX, ISO_8601_DATE_STRING_REGEX, JPEG_MIME_TYPE, JPEG_MIME_TYPES, JPG_MIME_TYPE, JSON_MIME_TYPE, KeyValueTypleValueFilter, LAT_LNG_PATTERN, LAT_LNG_PATTERN_MAX_PRECISION, LAT_LONG_100KM_PRECISION, LAT_LONG_100M_PRECISION, LAT_LONG_10CM_PRECISION, LAT_LONG_10KM_PRECISION, LAT_LONG_10M_PRECISION, LAT_LONG_1CM_PRECISION, LAT_LONG_1KM_PRECISION, LAT_LONG_1MM_PRECISION, LAT_LONG_1M_PRECISION, LAT_LONG_GRAINS_OF_SAND_PRECISION, LEADING_SLASHES_REGEX, MAP_IDENTITY, MARKDOWN_MIME_TYPE, MAX_BITWISE_SET_SIZE, MAX_LATITUDE_VALUE, MAX_LONGITUDE_VALUE, MINUTES_IN_DAY, MINUTES_IN_HOUR, MINUTE_OF_DAY_MAXMIMUM, MINUTE_OF_DAY_MINIUMUM, MIN_LATITUDE_VALUE, MIN_LONGITUDE_VALUE, MONTH_DAY_SLASH_DATE_STRING_REGEX, MS_IN_DAY, MS_IN_HOUR, MS_IN_MINUTE, MS_IN_SECOND, MS_IN_WEEK, MemoryStorageInstance, ModelRelationUtility, NOOP_MODIFIER, NUMBER_STRING_DENCODER_64, NUMBER_STRING_DENCODER_64_DIGITS, OAUTH_OOB_REDIRECT_URI, PDF_ENCRYPT_MARKER, PDF_EOF_MARKER, PDF_HEADER, PDF_MIME_TYPE, PHONE_EXTENSION_NUMBER_REGEX, PJPEG_MIME_TYPE, PNG_MIME_TYPE, PRIMATIVE_KEY_DENCODER_VALUE, PropertyDescriptorUtility, RAW_MIME_TYPE, REGEX_SPECIAL_CHARACTERS, REGEX_SPECIAL_CHARACTERS_SET, RelationChange, SECONDS_IN_DAY, SECONDS_IN_HOUR, SECONDS_IN_MINUTE, SHARED_MEMORY_STORAGE, SLASH_PATH_FILE_TYPE_SEPARATOR, SLASH_PATH_SEPARATOR, SORT_VALUE_EQUAL, SORT_VALUE_GREATER_THAN, SORT_VALUE_LESS_THAN, SPACE_JOINER, SPACE_STRING_SPLIT_JOIN, SPLIT_STRING_TREE_NODE_ROOT_VALUE, SVG_MIME_TYPE, ServerErrorResponse, SetDeltaChange, SimpleStorageObject, SlashPathPathMatcherPartCode, StorageObject, StorageObjectUtility, StoredDataError, SyncState, TIFF_MIME_TYPE, TIME_UNIT_LABEL_MAP, TIME_UNIT_SHORT_LABEL_MAP, TOTAL_LATITUDE_RANGE, TOTAL_LONGITUDE_RANGE, TOTAL_SPAN_OF_LONGITUDE, TRAILING_FILE_TYPE_SEPARATORS_REGEX, TRAILING_SLASHES_REGEX, TXT_MIME_TYPE, TimeAM, TimerCancelledError, TypedServiceRegistryInstance, UNLOADED_PAGE, UNSET_INDEX_NUMBER, US_STATE_CODE_STRING_REGEX, UTC_DATE_STRING_REGEX, UTC_TIMEZONE_STRING, UTF_8_START_CHARACTER, UTF_PRIVATE_USAGE_AREA_START, UnauthorizedServerErrorResponse, WEBP_MIME_TYPE, WEBSITE_TLD_DETECTION_REGEX, WEB_PROTOCOL_PREFIX_REGEX, XLSX_MIME_TYPE, XML_MIME_TYPE, YAML_MIME_TYPE, ZIP_CODE_STRING_REGEX, ZIP_FILE_MIME_TYPE, addHttpToUrl, addLatLngPoints, addMilliseconds, addModifiers, addPlusPrefixToNumber, addPrefix, addPrefixFunction, addSuffix, addSuffixFunction, addToSet, addToSetCopy, addToSplitStringTree, addTrailingSlash, allFalsyOrEmptyKeys, allIndexesInIndexRange, allKeyValueTuples, allMaybeSoKeys, allNonUndefinedKeys, allObjectsAreEqual, allValuesAreMaybeNot, allValuesAreNotMaybe, allowValueOnceFilter, applicationFileExtensionForMimeType, applyBestFit, applySplitStringTreeWithMultipleValues, applyToMultipleFields, approximateTimerEndDate, areEqualContext, areEqualPOJOValues, areEqualPOJOValuesUsingPojoFilter, arrayContainsDuplicateValue, arrayContentsDiffer, arrayDecision, arrayDecisionFunction, arrayFactory, arrayInputFactory, arrayToLowercase, arrayToMap, arrayToObject, arrayToUppercase, asArray, asDecisionFunction, asGetter, asIndexRangeCheckFunctionConfig, asIterable, asMinuteOfDay, asNonEmptyArray, asNumber, asObjectCopyFactory, asPromise, asSet, assignValuesToPOJO, assignValuesToPOJOFunction, authClaims, authRoleClaimsService, authRolesSetHasRoles, baseWebsiteUrl, batch, batchCalc, bitwiseObjectDencoder, bitwiseObjectEncoder, bitwiseObjectdecoder, bitwiseSetDecoder, bitwiseSetDencoder, booleanFactory, booleanKeyArrayUtility, boundNumber, boundNumberFunction, boundToRectangle, breadthFirstExploreTreeTraversalFactoryFunction, bufferHasValidPdfMarkings, build, cachedGetter, calculateExpirationDate, camelOrPascalToScreamingSnake, capLatValue, capitalizeFirstLetter, caseInsensitiveFilterByIndexOfDecisionFactory, caseInsensitiveString, catchAllHandlerKey, chainMapFunction, chainMapSameFunctions, characterPrefixSuffixInstance, checkAnyHaveExpired, checkAtleastOneNotExpired, coerceToEmailParticipants, combineMaps, compareEqualityWithValueFromItemsFunction, compareEqualityWithValueFromItemsFunctionFactory, compareFnOrder, compareStrings, compareStringsNumeric, compareWithMappedValuesFunction, computeNextFractionalHour, computeNextFreeIndexFunction, computeNextFreeIndexOnSortedValuesFunction, concatArrays, concatArraysUnique, containsAllStringsAnyCase, containsAllValues, containsAnyStringAnyCase, containsAnyValue, containsAnyValueFromSet, containsNoValueFromSet, containsNoneOfValue, containsStringAnyCase, convertEmailParticipantStringToParticipant, convertMaybeToArray, convertMaybeToNonEmptyArray, convertParticipantToEmailParticipantString, convertTimeDuration, convertToArray, copyArray, copyField, copyLatLngBound, copyLatLngPoint, copyObject, copySetAndDo, countAllInNestedArray, countPOJOKeys, countPOJOKeysFunction, cronExpressionRepeatingEveryNMinutes, cssClassesSet, cssTokenVar, cssVariableVar, cutString, cutStringFunction, cutToPrecision, cutValueToInteger, cutValueToPrecision, cutValueToPrecisionFunction, dateFromDateOrTimeMillisecondsNumber, dateFromDateOrTimeSecondsNumber, dateFromLogicalDate, dateFromMinuteOfDay, dateOrMillisecondsToDate, dateToHoursAndMinutes, dateToMinuteOfDay, dayOfWeek, daysOfWeekArray, daysOfWeekFromEnabledDays, daysOfWeekNameFunction, daysOfWeekNameMap, decisionFunction, decodeHashedValues, decodeHashedValuesWithDecodeMap, decodeModelKeyTypePair, decodeRadix36Number, defaultFilterFromPOJOFunctionNoCopy, defaultForwardFunctionFactory, defaultLatLngPoint, defaultLatLngString, dencodeBitwiseSet, depthFirstExploreTreeTraversalFactoryFunction, diffLatLngBoundPoints, diffLatLngPoints, documentFileExtensionForMimeType, dollarAmountString, dollarAmountStringWithUnitFunction, e164PhoneNumberExtensionPair, e164PhoneNumberFromE164PhoneNumberExtensionPair, enabledDaysFromDaysOfWeek, encodeBitwiseSet, encodeModelKeyTypePair, encodeRadix36Number, errorMessageContainsString, errorMessageContainsStringFunction, escapeStringCharactersFunction, escapeStringForRegex, excludeValues, excludeValuesFromArray, excludeValuesFromSet, existsInIterable, expandArrayMapTuples, expandArrayValueTuples, expandFlattenTreeFunction, expandIndexSet, expandSlashPathPathMatcherPartToDecisionFunctions, expandTreeFunction, expandTrees, expirationDetails, exploreTreeFunction, exponentialPromiseRateLimiter, extendLatLngBound, fileExtensionForMimeType, filterAndMapFunction, filterEmptyArrayValues, filterEmptyPojoValues, filterFalsyAndEmptyValues, filterFromIterable, filterFromPOJO, filterFromPOJOFunction, filterKeyValueTupleFunction, filterKeyValueTuples, filterKeyValueTuplesFunction, filterKeyValueTuplesInputToFilter, filterKeysOnPOJOFunction, filterMaybeArrayFunction, filterMaybeArrayValues, filterNullAndUndefinedValues, filterOnlyUndefinedValues, filterTuplesOnPOJOFunction, filterUndefinedValues, filterUniqueByIndex, filterUniqueCaseInsensitiveStrings, filterUniqueFunction, filterUniqueTransform, filterUniqueValues, filterValuesByDistance, filterValuesByDistanceNoOrder, filterValuesToSet, filterValuesUsingSet, filteredPage, findAllCharacterOccurences, findAllCharacterOccurencesFunction, findBest, findBestIndexMatch, findBestIndexMatchFunction, findBestIndexSetPair, findBestSplitStringTreeChildMatch, findBestSplitStringTreeChildMatchPath, findBestSplitStringTreeMatch, findBestSplitStringTreeMatchPath, findFirstCharacterOccurence, findInIterable, findIndexOfFirstDuplicateValue, findItemsByIndex, findNext, findPOJOKeys, findPOJOKeysFunction, findStringsRegexString, findToIndexSet, findValuesFrom, firstAndLastCharacterOccurrence, firstAndLastValue, firstValue, firstValueFromIterable, fitToIndexRangeFunction, fixExtraQueryParameters, fixMultiSlashesInSlashPath, flattenArray, flattenArrayOrValueArray, flattenArrayToSet, flattenArrayUnique, flattenArrayUniqueCaseInsensitiveStrings, flattenObject, flattenTree, flattenTreeToArray, flattenTreeToArrayFunction, flattenWhitespace, forEachInIterable, forEachKeyValue, forEachKeyValueOnPOJOFunction, forEachWithArray, forwardFunction, fractionalHoursToMinutes, generateIfDoesNotExist, generatePkceCodeChallenge, generatePkceCodeVerifier, getArrayNextIndex, getBaseLog, getDayOffset, getDayTomorrow, getDayYesterday, getDaysOfWeekNames, getFunctionType, getNextDay, getNextPageNumber, getOverlappingRectangle, getPageNumber, getPreviousDay, getValueFromGetter, groupValues, handlerBindAccessor, handlerConfigurerFactory, handlerFactory, handlerMappedSetFunction, handlerMappedSetFunctionFactory, handlerSetFunction, hasDifferentStringsNoCase, hasDifferentValues, hasHttpPrefix, hasNonNullValue, hasPortNumber, hasSameTimezone, hasSameValues, hasValueFunction, hasValueOrNotEmpty, hasValueOrNotEmptyObject, hasWebsiteDomain, hasWebsiteTopLevelDomain, hashSetForIndexed, hourToFractionalHour, hoursAndMinutesToString, hoursAndMinutesToTimeUnit, idBatchFactory, imageFileExtensionForMimeType, inMemoryAsyncKeyedValueCache, inMemoryAsyncValueCache, incrementingNumberFactory, indexDeltaGroup, indexDeltaGroupFunction, indexRange, indexRangeCheckFunction, indexRangeCheckReaderFunction, indexRangeForArray, indexRangeOverlapsIndexRange, indexRangeOverlapsIndexRangeFunction, indexRangeReaderPairFactory, indexRefMap, indexedValuesArrayAccessorFactory, insertIntoBooleanKeyArray, invertBooleanReturnFunction, invertDecision, invertFilter, invertMaybeBoolean, invertStringRecord, isAllowed, isClassLikeType, isCompleteUnitedStatesAddress, isConsideredUtcTimezoneString, isDate, isDefaultLatLngPoint, isDefaultLatLngPointValue, isDefaultReadableError, isDefinedAndNotFalse, isDollarAmountString, isE164PhoneNumber, isE164PhoneNumberWithExtension, isEmptyIterable, isEqualContext, isEqualDate, isEqualToValueDecisionFunction, isEvenNumber, isExpired, isFalseBooleanKeyArray, isFinalPage, isGetter, isHex, isHexWithByteLength, isHourOfDay, isISO8601DateString, isISO8601DayString, isISO8601DayStringStart, isInAllowedDaysOfWeekSet, isInNumberBoundFunction, isInSetDecisionFunction, isIndexNumberInIndexRange, isIndexNumberInIndexRangeFunction, isIndexRangeInIndexRange, isIndexRangeInIndexRangeFunction, isIterable, isKnownHttpWebsiteProtocol, isLatLngBound, isLatLngBoundWithinLatLngBound, isLatLngPoint, isLatLngPointWithinLatLngBound, isLatLngString, isLogicalDateStringCode, isMapIdentityFunction, isMaybeNot, isMaybeNotOrTrue, isMaybeSo, isMinuteOfDay, isModelKey, isMonthDaySlashDate, isNonClassFunction, isNotFalse, isNotNullOrEmptyString, isNumberDivisibleBy, isObjectWithConstructor, isOddNumber, isPast, isPdfPasswordProtected, isPromise, isPromiseLike, isSameLatLngBound, isSameLatLngPoint, isSameNonNullValue, isSameVector, isSelectedDecisionFunctionFactory, isSelectedIndexDecisionFunction, isServerError, isSlashPathFile, isSlashPathFolder, isSlashPathTypedFile, isStandardInternetAccessibleWebsiteUrl, isStringOrTrue, isThrottled, isTrueBooleanKeyArray, isUTCDateString, isUnderThreshold, isUniqueKeyedFunction, isUsStateCodeString, isValidLatLngPoint, isValidLatitude, isValidLongitude, isValidNumberBound, isValidPhoneExtensionNumber, isValidSlashPath, isWebsiteUrl, isWebsiteUrlWithPrefix, isWithinLatLngBoundFunction, isolateSlashPath, isolateSlashPathFunction, isolateWebsitePathFunction, itemCountForBatchIndex, iterableToArray, iterableToMap, iterableToSet, iterablesAreSetEquivalent, iterate, iterateFilteredPages, joinHostAndPort, joinStrings, joinStringsInstance, joinStringsWithCommas, joinStringsWithSpaces, keepCharactersAfterFirstCharacterOccurence, keepCharactersAfterFirstCharacterOccurenceFunction, keepFromSetCopy, keepValuesFromArray, keepValuesFromSet, keyValueMapFactory, labeledValueMap, lastValue, latLngBound, latLngBoundCenterPoint, latLngBoundEastBound, latLngBoundFromInput, latLngBoundFullyWrapsMap, latLngBoundFunction, latLngBoundNorthBound, latLngBoundNorthEastPoint, latLngBoundNorthWestPoint, latLngBoundOverlapsLatLngBound, latLngBoundSouthBound, latLngBoundSouthEastPoint, latLngBoundSouthWestPoint, latLngBoundStrictlyWrapsMap, latLngBoundTuple, latLngBoundTupleFunction, latLngBoundWestBound, latLngBoundWrapsMap, latLngDataPointFunction, latLngPoint, latLngPointFromString, latLngPointFunction, latLngPointPrecisionFunction, latLngString, latLngStringFunction, latLngTuple, latLngTupleFunction, limitArray, lonLatTuple, lowercaseFirstLetter, mailToUrlString, makeBestFit, makeCopyModelFieldFunction, makeDateMonthForMonthOfYear, makeDefaultNonConcurrentTaskKeyFactory, makeGetter, makeHandler, makeHashDecodeMap, makeKeyPairs, makeModelConversionFieldValuesFunction, makeModelMap, makeModelMapFunctions, makeMultiModelKeyMap, makeTimer, makeValuesGroupMap, makeWithFactory, makeWithFactoryInput, mapArrayFunction, mapFunctionOutput, mapFunctionOutputPair, mapGetter, mapGetterFactory, mapIdentityFunction, mapIterable, mapKeysIntersectionObjectToArray, mapMaybeFunction, mapObjectKeysFunction, mapObjectKeysToLowercase, mapObjectMap, mapObjectMapFunction, mapObjectToTargetObject, mapPromiseOrValue, mapToObject, mapToTuples, mapValuesToSet, mappedUseAsyncFunction, mappedUseFunction, mapsHaveSameKeys, maybeMergeModelModifiers, maybeMergeModifiers, maybeModifierMapToFunction, maybeSet, memoizeAsyncKeyedValueCache, memoizeAsyncValueCache, mergeArrays, mergeArraysIntoArray, mergeAsyncKeyedValueCaches, mergeAsyncValueCaches, mergeFilterFunctions, mergeModifiers, mergeObjects, mergeObjectsFunction, mergeSlashPaths, messageFromError, millisecondsToMinutes, millisecondsToMinutesAndSeconds, millisecondsToTimeUnit, mimeTypeForApplicationFileExtension, mimeTypeForDocumentFileExtension, mimeTypeForFileExtension, mimeTypeForImageFileExtension, minAndMaxFunction, minAndMaxIndex, minAndMaxIndexFunction, minAndMaxIndexItemsFunction, minAndMaxNumber, minutesToFractionalHours, minutesToHoursAndMinutes, modelFieldConversions, modelFieldMapFunction, modelFieldMapFunctions, modelTypeDataPairFactory, modifier, modifierMapToFunction, modifyModelMapFunction, modifyModelMapFunctions, monthDaySlashDateToDateString, monthOfYearFromDate, monthOfYearFromDateMonth, monthOfYearFromUTCDate, multiKeyValueMapFactory, multiValueMapBuilder, neMostLatLngPoint, nearestDivisibleValues, noop, numberStringDencoder, numberStringDencoderDecodedNumberValueFunction, numberStringDencoderEncodedStringValueFunction, numberStringDencoderFunction, objectCopyFactory, objectDeltaArrayCompressor, objectFieldEqualityChecker, objectFlatMergeMatrix, objectHasKey, objectHasKeys, objectHasNoKeys, objectIsEmpty, objectKeyEqualityComparatorFunction, objectKeysEqualityComparatorFunction, objectMergeMatrix, objectToMap, overlapsLatLngBoundFunction, overrideInObject, overrideInObjectFunctionFactory, padStartFunction, pairGroupValues, parseISO8601DayStringToUTCDate, partialServerError, MAP_IDENTITY as passThrough, percentNumberFromDecimal, percentNumberToDecimal, performAsyncTask, performAsyncTasks, performBatchLoop, performMakeLoop, performTaskCountLoop, performTaskLoop, performTasksFromFactoryInParallelFunction, performTasksInParallel, performTasksInParallelFunction, pickOneRandomly, poll, primativeKeyDencoder, primativeKeyDencoderMap, primativeKeyStringDencoder, primativeValuesDelta, promiseReference, protectedFactory, pushArrayItemsIntoArray, pushElementOntoArray, pushItemOrArrayItemsIntoArray, randomArrayFactory, randomArrayIndex, randomBoolean, randomEmailFactory, randomFromArrayFactory, randomLatLngFactory, randomLatLngFromCenterFactory, randomNumber, randomNumberFactory, randomPhoneNumberFactory, randomPickFactory, range, rangedIndexedValuesArrayAccessorFactory, rangedIndexedValuesArrayAccessorInfoFactory, readBooleanKeySafetyWrap, readDomainFromEmailAddress, readDomainsFromEmailAddresses, readEmailDomainFromUrlOrEmailAddress, readIndexNumber, readKeysFrom, readKeysFromFilterUniqueFunctionAdditionalKeys, readKeysFromFilterUniqueFunctionAdditionalKeysInput, readKeysFunction, readKeysSetFrom, readKeysSetFunction, readKeysToMap, readModelKey, readModelKeyFromObject, readModelKeys, readModelKeysFromObjects, readMultipleKeysToMap, readPortNumber, readUniqueModelKey, readWebsiteProtocol, readableError, readableStreamToBase64, readableStreamToBuffer, readableStreamToStringFunction, rectangleOverlapsRectangle, reduceBooleansFn, reduceBooleansWithAnd, reduceBooleansWithAndFn, reduceBooleansWithOr, reduceBooleansWithOrFn, reduceNumbers, reduceNumbersFn, reduceNumbersWithAdd, reduceNumbersWithAddFn, reduceNumbersWithMax, reduceNumbersWithMaxFn, reduceNumbersWithMin, reduceNumbersWithMinFn, removeByKeyFromBooleanKeyArray, removeCharactersAfterFirstCharacterOccurence, removeCharactersAfterFirstCharacterOccurenceFunction, removeExtensionFromPhoneNumber, removeFirstMatchingSuffix, removeFromBooleanKeyArray, removeFromSet, removeFromSetCopy, removeHttpFromUrl, removeModelsWithKey, removeModelsWithSameKey, removeModifiers, removeSuffix, removeTrailingFileTypeSeparators, removeTrailingSlashes, removeValuesAtIndexesFromArrayCopy, removeWebProtocolPrefix, repeatString, replaceCharacterAtIndexIf, replaceCharacterAtIndexWith, replaceInvalidFilePathTypeSeparatorsInSlashPath, replaceInvalidFilePathTypeSeparatorsInSlashPathFunction, replaceLastCharacterIf, replaceLastCharacterIfIsFunction, replaceMultipleFilePathsInSlashPath, replaceStringsFunction, requireModelKey, resetPeriodPromiseRateLimiter, restoreOrder, restoreOrderWithValues, reverseCompareFn, roundNumberToStepFunction, roundNumberUpToStep, roundToPrecision, roundToPrecisionFunction, roundingFunction, runAsyncTaskForValue, runAsyncTasksForValues, runNamedAsyncTasks, runNamedAsyncTasksFunction, safeCompareEquality, safeEqualityComparatorFunction, safeFindBestIndexMatch, screamingSnakeToCamelCase, searchStringFilterFunction, secondsToMinutesAndSeconds, selectiveFieldEncryptor, separateValues, separateValuesToSets, sequentialIncrementingNumberStringModelIdFactory, serverError, setContainsAllValues, setContainsAnyValue, setContainsNoneOfValue, setDeltaChangeKeys, setDeltaFunction, setHasValueFunction, setIncludes, setIncludesFunction, setKeysOnMap, setWebProtocolPrefix, setsAreEquivalent, simpleSortValuesFunctionWithSortRef, simplifyWhitespace, slashPathDetails, slashPathDirectoryTree, slashPathFactory, slashPathFolder, slashPathFolderFactory, slashPathInvalidError, slashPathName, slashPathParts, slashPathPathMatcher, slashPathPathMatcherConfig, slashPathStartTypeFactory, slashPathSubPathMatcher, slashPathType, slashPathValidationFactory, sliceIndexRangeFunction, sliceStringFunction, sortAscendingIndexNumberRefFunction, sortByIndexAscendingCompareFunction, sortByIndexRangeAscendingCompareFunction, sortByLabelFunction, sortByNumberFunction, sortByStringFunction, sortCompareNumberFunction, sortNumbersAscendingFunction, sortValues, sortValuesFunctionOrMapIdentityWithSortRef, sortValuesFunctionWithSortRef, spaceSeparatedCssClasses, splitCommaSeparatedString, splitCommaSeparatedStringToSet, splitFront, splitJoinNameString, splitJoinRemainder, splitStringAtFirstCharacterOccurence, splitStringAtFirstCharacterOccurenceFunction, splitStringAtIndex, splitStringTreeFactory, startOfDayForSystemDateInUTC, startOfDayForUTCDateInUTC, stepsFromIndex, stepsFromIndexFunction, stringCharactersToIndexRecord, stringContains, stringFactoryFromFactory, stringFromDateFactory, stringFromTimeFactory, stringSplitJoinInstance, stringToBoolean, stringToLowercaseFunction, stringToUppercaseFunction, stringTrimFunction, stripObject, stripObjectFunction, sumOfIntegersBetween, swMostLatLngPoint, symmetricDifferenceArray, symmetricDifferenceArrayBetweenSets, symmetricDifferenceWithModels, takeFront, takeLast, takeValuesFromIterable, telUrlString, telUrlStringForE164PhoneNumberPair, terminatingFactoryFromArray, throwKeyIsRequired, timeDurationToHoursAndMinutes, timeDurationToMilliseconds, timePeriodCounter, timeUnitToMilliseconds, toAbsoluteSlashPathStartType, toCaseInsensitiveStringArray, toMinuteOfDay, toModelFieldConversions, toModelMapFunctions, toReadableError, toRelativeSlashPathStartType, toggleInSet, toggleInSetCopy, toggleTimerRunning, transformNumberFunction, transformNumberFunctionConfig, transformStringFunction, transformStringFunctionConfig, transformStrings, trimArray, trueOrFalseString, tryConvertToE164PhoneNumber, tryWithPromiseFactoriesFunction, typedServiceRegistry, unique, uniqueCaseInsensitiveStrings, uniqueCaseInsensitiveStringsSet, uniqueKeys, uniqueModels, unitedStatesAddressString, unixDateTimeSecondsNumberForNow, unixDateTimeSecondsNumberFromDate, unixDateTimeSecondsNumberFromDateOrTimeNumber, unixDateTimeSecondsNumberToDate, unixMillisecondsNumberToDate, updateMaybeValue, urlWithoutParameters, useAsync, useCallback, useContextFunction, useIterableOrValue, useModelOrKey, usePromise, useValue, validLatLngPoint, validLatLngPointFunction, valueAtIndex, valuesAreBothNullishOrEquivalent, valuesFromPOJO, valuesFromPOJOFunction, vectorMinimumSizeResizeFunction, vectorsAreEqual, waitForMs, websiteDomainAndPathPair, websiteDomainAndPathPairFromWebsiteUrl, websitePathAndQueryPair, websitePathFromWebsiteDomainAndPath, websitePathFromWebsiteUrl, websiteUrlDetails, websiteUrlFromPaths, wrapIndexRangeFunction, wrapLatLngPoint, wrapLngValue, wrapMapFunctionOutput, wrapNumberFunction, wrapTuples, wrapUseAsyncFunction, wrapUseFunction };
25210
+ /**
25211
+ * Spec-file naming conventions for API test files.
25212
+ *
25213
+ * Downstream Firebase Functions API apps keep their tests under
25214
+ * `<apiDir>/src/app/function/<group>/`, with each spec file named so that
25215
+ * "what does this test, and where do I add the next one?" can be answered
25216
+ * from the filename alone. The canonical forms are:
25217
+ *
25218
+ * - `<group>.crud.spec.ts` — non-scenario tests of the CRUD function map for the group.
25219
+ * - `<group>.crud.<sub>[.<sub>...].spec.ts` — CRUD tests focused on a sub-area
25220
+ * (e.g. `job.crud.requirement.spec.ts`).
25221
+ * - `<group>.scenario.spec.ts` — generic multi-step scenario tests using fixture
25222
+ * chains.
25223
+ * - `<group>.scenario.<sub>[.<sub>...].spec.ts` — focused scenario sub-bucket
25224
+ * (e.g. `job.scenario.requirement.worker.state.spec.ts`).
25225
+ *
25226
+ * Drift forms (still parsed, but flagged with a rename suggestion):
25227
+ * - `<group>.<sub>.crud.spec.ts` — `crud` placed after a subgroup.
25228
+ * Suggestion: `<group>.crud.<sub>.spec.ts`.
25229
+ * - `<group>.<sub>.scenario.spec.ts` — `scenario` placed after a subgroup
25230
+ * (e.g. `worker.payroll.scenario.spec.ts`, `school.job.publish.scenario.spec.ts`).
25231
+ * Suggestion: `<group>.scenario.<sub>.spec.ts`.
25232
+ * - `<group>.<rest>.spec.ts` — no `crud` / `scenario` segment at all
25233
+ * (e.g. `worker.system.spec.ts`). Defaults to a scenario rename suggestion
25234
+ * because the missing bucket is more often scenario-like.
25235
+ *
25236
+ * This module is pure: classification and canonical-path rendering live
25237
+ * here, with no disk I/O. It's consumed by `@dereekb/dbx-components-mcp`
25238
+ * (the `dbx_model_test_*` tool cluster) and by
25239
+ * `@dereekb/firebase/eslint` (the `require-canonical-api-spec-filename`
25240
+ * and `require-api-crud-spec-for-group` rules).
25241
+ */ /**
25242
+ * Whether a given kind is on-convention. Drift kinds (`-misplaced`,
25243
+ * `no-bucket`) are still parseable but earn a warning + rename suggestion.
25244
+ */ var CANONICAL_KINDS = [
25245
+ 'crud',
25246
+ 'crud-subgroup',
25247
+ 'scenario',
25248
+ 'scenario-subgroup'
25249
+ ];
25250
+ var SPEC_SUFFIX = '.spec.ts';
25251
+ /**
25252
+ * Classifies a spec filename against the conventions for the given parent
25253
+ * folder. Pure function — never touches the filesystem.
25254
+ *
25255
+ * @param config - Inputs.
25256
+ * @param config.filename - Bare filename including the `.spec.ts` suffix
25257
+ * (e.g. `job.scenario.requirement.spec.ts`).
25258
+ * @param config.parentFolderName - Name of the directory containing the file
25259
+ * (e.g. `job`). Used to detect cross-group misplacement.
25260
+ * @returns The classification.
25261
+ */ function classifySpecFile(config) {
25262
+ var filename = config.filename, parentFolderName = config.parentFolderName;
25263
+ var result;
25264
+ if (!filename.endsWith(SPEC_SUFFIX)) {
25265
+ result = {
25266
+ filename: filename,
25267
+ group: '',
25268
+ kind: 'non-spec',
25269
+ subgroups: [],
25270
+ isCanonical: false
25271
+ };
25272
+ } else {
25273
+ var _parts_;
25274
+ var stem = filename.slice(0, -SPEC_SUFFIX.length);
25275
+ var parts = stem.split('.');
25276
+ var group = (_parts_ = parts[0]) !== null && _parts_ !== void 0 ? _parts_ : '';
25277
+ if (group !== parentFolderName) {
25278
+ result = {
25279
+ filename: filename,
25280
+ group: group,
25281
+ kind: 'non-group',
25282
+ subgroups: [],
25283
+ isCanonical: false
25284
+ };
25285
+ } else {
25286
+ var rest = parts.slice(1);
25287
+ result = classifyRemainingSegments({
25288
+ filename: filename,
25289
+ group: group,
25290
+ rest: rest
25291
+ });
25292
+ }
25293
+ }
25294
+ return result;
25295
+ }
25296
+ function classifyRemainingSegments(config) {
25297
+ var filename = config.filename, group = config.group, rest = config.rest;
25298
+ var result;
25299
+ var crudIdx = rest.indexOf('crud');
25300
+ var scenarioIdx = rest.indexOf('scenario');
25301
+ if (crudIdx === 0) {
25302
+ if (rest.length === 1) {
25303
+ result = {
25304
+ filename: filename,
25305
+ group: group,
25306
+ kind: 'crud',
25307
+ subgroups: [],
25308
+ isCanonical: true
25309
+ };
25310
+ } else {
25311
+ result = {
25312
+ filename: filename,
25313
+ group: group,
25314
+ kind: 'crud-subgroup',
25315
+ subgroups: rest.slice(1),
25316
+ isCanonical: true
25317
+ };
25318
+ }
25319
+ } else if (scenarioIdx === 0) {
25320
+ if (rest.length === 1) {
25321
+ result = {
25322
+ filename: filename,
25323
+ group: group,
25324
+ kind: 'scenario',
25325
+ subgroups: [],
25326
+ isCanonical: true
25327
+ };
25328
+ } else {
25329
+ result = {
25330
+ filename: filename,
25331
+ group: group,
25332
+ kind: 'scenario-subgroup',
25333
+ subgroups: rest.slice(1),
25334
+ isCanonical: true
25335
+ };
25336
+ }
25337
+ } else if (crudIdx > 0) {
25338
+ var subgroups = rest.filter(function(_, i) {
25339
+ return i !== crudIdx;
25340
+ });
25341
+ var recommendedRename = buildCanonicalFilename({
25342
+ group: group,
25343
+ bucket: 'crud',
25344
+ subgroups: subgroups
25345
+ });
25346
+ result = {
25347
+ filename: filename,
25348
+ group: group,
25349
+ kind: 'crud-misplaced',
25350
+ subgroups: subgroups,
25351
+ isCanonical: false,
25352
+ recommendedRename: recommendedRename,
25353
+ driftReason: '`crud` segment is not directly after the group name.'
25354
+ };
25355
+ } else if (scenarioIdx > 0) {
25356
+ var subgroups1 = rest.filter(function(_, i) {
25357
+ return i !== scenarioIdx;
25358
+ });
25359
+ var recommendedRename1 = buildCanonicalFilename({
25360
+ group: group,
25361
+ bucket: 'scenario',
25362
+ subgroups: subgroups1
25363
+ });
25364
+ result = {
25365
+ filename: filename,
25366
+ group: group,
25367
+ kind: 'scenario-misplaced',
25368
+ subgroups: subgroups1,
25369
+ isCanonical: false,
25370
+ recommendedRename: recommendedRename1,
25371
+ driftReason: '`scenario` segment is not directly after the group name.'
25372
+ };
25373
+ } else if (rest.length === 0) {
25374
+ var recommendedRename2 = buildCanonicalFilename({
25375
+ group: group,
25376
+ bucket: 'scenario',
25377
+ subgroups: []
25378
+ });
25379
+ result = {
25380
+ filename: filename,
25381
+ group: group,
25382
+ kind: 'no-bucket',
25383
+ subgroups: [],
25384
+ isCanonical: false,
25385
+ recommendedRename: recommendedRename2,
25386
+ driftReason: 'Missing `crud` or `scenario` segment.'
25387
+ };
25388
+ } else {
25389
+ var recommendedRename3 = buildCanonicalFilename({
25390
+ group: group,
25391
+ bucket: 'scenario',
25392
+ subgroups: rest
25393
+ });
25394
+ result = {
25395
+ filename: filename,
25396
+ group: group,
25397
+ kind: 'no-bucket',
25398
+ subgroups: rest,
25399
+ isCanonical: false,
25400
+ recommendedRename: recommendedRename3,
25401
+ driftReason: 'Missing `crud` or `scenario` segment — defaulting suggestion to `scenario`.'
25402
+ };
25403
+ }
25404
+ return result;
25405
+ }
25406
+ /**
25407
+ * Renders a canonical spec filename for the given group + bucket + subgroup
25408
+ * chain. Pure data — used both by drift remediation and by the
25409
+ * `recommendSpecPath()` helper below.
25410
+ *
25411
+ * @param config - Inputs.
25412
+ * @param config.group - The model-group name (e.g. `job`).
25413
+ * @param config.bucket - `crud` or `scenario`.
25414
+ * @param config.subgroups - Optional ordered subgroup segments
25415
+ * (e.g. `['requirement','worker']`).
25416
+ * @returns The canonical filename (e.g. `job.scenario.requirement.worker.spec.ts`).
25417
+ */ function buildCanonicalFilename(config) {
25418
+ var group = config.group, bucket = config.bucket, subgroups = config.subgroups;
25419
+ var tail = subgroups.length === 0 ? '' : ".".concat(subgroups.join('.'));
25420
+ return "".concat(group, ".").concat(bucket).concat(tail, ".spec.ts");
25421
+ }
25422
+ /**
25423
+ * Renders the canonical relative path for a spec file under a given API app.
25424
+ * The shape mirrors the layout used throughout the workspace:
25425
+ * `<apiDir>/src/app/function/<group>/<filename>`.
25426
+ *
25427
+ * @param config - Inputs.
25428
+ * @param config.apiDir - The relative API-app directory
25429
+ * (e.g. `apps/hellosubs-api`).
25430
+ * @param config.group - The model-group name.
25431
+ * @param config.bucket - `crud` or `scenario`.
25432
+ * @param config.subgroups - Optional subgroup chain.
25433
+ * @returns The canonical relative spec-file path.
25434
+ */ function recommendSpecPath(config) {
25435
+ var filename = buildCanonicalFilename({
25436
+ group: config.group,
25437
+ bucket: config.bucket,
25438
+ subgroups: config.subgroups
25439
+ });
25440
+ return "".concat(config.apiDir, "/src/app/function/").concat(config.group, "/").concat(filename);
25441
+ }
25442
+ /**
25443
+ * Returns the two canonical buckets for a given model group, with rendered
25444
+ * paths and one-line summaries. Used by the list-app formatter.
25445
+ *
25446
+ * @param config - Inputs.
25447
+ * @param config.apiDir - Relative API-app directory.
25448
+ * @param config.group - Model-group name.
25449
+ * @returns The crud + scenario recommendations.
25450
+ */ function recommendBucketsForGroup(config) {
25451
+ var crudPath = recommendSpecPath({
25452
+ apiDir: config.apiDir,
25453
+ group: config.group,
25454
+ bucket: 'crud',
25455
+ subgroups: []
25456
+ });
25457
+ var scenarioPath = recommendSpecPath({
25458
+ apiDir: config.apiDir,
25459
+ group: config.group,
25460
+ bucket: 'scenario',
25461
+ subgroups: []
25462
+ });
25463
+ return [
25464
+ {
25465
+ bucket: 'crud',
25466
+ label: 'CRUD',
25467
+ canonicalPath: crudPath,
25468
+ summary: 'Non-scenario tests of the CRUD function map — create/read/update/delete + permission/error paths. Add focused buckets as `' + config.group + '.crud.<sub>.spec.ts`.'
25469
+ },
25470
+ {
25471
+ bucket: 'scenario',
25472
+ label: 'Scenario',
25473
+ canonicalPath: scenarioPath,
25474
+ summary: 'Multi-step scenarios using fixture chains (mirrors real workflows). Add focused buckets as `' + config.group + '.scenario.<sub>.spec.ts` (chain subgroups freely, e.g. `.scenario.requirement.worker.spec.ts`).'
25475
+ }
25476
+ ];
25477
+ }
25478
+
25479
+ export { ALL_DOUBLE_SLASHES_REGEX, ALL_SLASHES_REGEX, ALL_SLASH_PATH_FILE_TYPE_SEPARATORS_REGEX, ALL_TIME_UNITS, APPLICATION_FILE_EXTENSION_TO_MIME_TYPES_RECORD, APPLICATION_MIME_TYPES_TO_FILE_EXTENSIONS_RECORD, ASSERTION_ERROR_CODE, ASSERTION_HANDLER, AUTH_ADMIN_ROLE, AUTH_ONBOARDED_ROLE, AUTH_TOS_SIGNED_ROLE, AUTH_USER_ROLE, AbstractUniqueModel, Assert, AssertMax, AssertMin, AssertionError, AssertionIssueHandler, BooleanStringKeyArrayUtility, CANONICAL_KINDS, CATCH_ALL_HANDLE_RESULT_KEY, COMMA_JOINER, COMMA_STRING_SPLIT_JOIN, CSV_MIME_TYPE, CUT_VALUE_TO_ZERO_PRECISION, DASH_CHARACTER_PREFIX_INSTANCE, DATE_NOW_VALUE, DAYS_IN_WEEK, DAYS_IN_YEAR, DEFAULT_AUTH_ROLE_CLAIMS_CLAIM_VALUE, DEFAULT_AUTH_ROLE_CLAIMS_EMPTY_VALUE, DEFAULT_CUT_STRING_END_TEXT, DEFAULT_ENCRYPTED_FIELD_PREFIX, DEFAULT_LAT_LNG_STRING_VALUE, DEFAULT_NUMBER_STRING_DENCODER_64_NEGATIVE_PREFIX, DEFAULT_RANDOM_EMAIL_FACTORY_CONFIG, DEFAULT_RANDOM_PHONE_NUMBER_FACTORY_CONFIG, DEFAULT_READABLE_ERROR_CODE, DEFAULT_SLASH_PATH_ILLEGAL_CHARACTERS, DEFAULT_SLASH_PATH_ILLEGAL_CHARACTER_REPLACEMENT, DEFAULT_SLASH_PATH_PATH_MATCHER_NON_MATCHING_FILL_VALUE, DEFAULT_UNKNOWN_MODEL_TYPE_STRING, DOCUMENT_FILE_EXTENSION_TO_MIME_TYPES_RECORD, DOCUMENT_MIME_TYPES_TO_FILE_EXTENSIONS_RECORD, DOCX_MIME_TYPE, DOLLAR_AMOUNT_PRECISION, DOLLAR_AMOUNT_STRING_REGEX, DataDoesNotExistError, DataIsExpiredError, Day, DestroyFunctionObject, E164PHONE_NUMBER_REGEX, E164PHONE_NUMBER_WITH_EXTENSION_REGEX, E164PHONE_NUMBER_WITH_OPTIONAL_EXTENSION_REGEX, ExploreTreeVisitNodeDecision, FINAL_PAGE, FIRST_PAGE, FRACTIONAL_HOURS_PRECISION_FUNCTION, FlattenTreeAddNodeDecision, FullStorageObject, GIF_MIME_TYPE, HAS_PORT_NUMBER_REGEX, HAS_WEBSITE_DOMAIN_NAME_REGEX, HEIF_MIME_TYPE, HEX_PATTERN, HOURS_IN_DAY, HOUR_OF_DAY_MAXMIMUM, HOUR_OF_DAY_MINIUMUM, HTML_MIME_TYPE, HTTP_OR_HTTPS_REGEX, HashSet, IMAGE_FILE_EXTENSION_TO_MIME_TYPES_RECORD, IMAGE_MIME_TYPES_TO_FILE_EXTENSIONS_RECORD, ISO8601_DAY_STRING_REGEX, ISO8601_DAY_STRING_START_REGEX, ISO_8601_DATE_STRING_REGEX, JPEG_MIME_TYPE, JPEG_MIME_TYPES, JPG_MIME_TYPE, JSON_MIME_TYPE, KeyValueTypleValueFilter, LAT_LNG_PATTERN, LAT_LNG_PATTERN_MAX_PRECISION, LAT_LONG_100KM_PRECISION, LAT_LONG_100M_PRECISION, LAT_LONG_10CM_PRECISION, LAT_LONG_10KM_PRECISION, LAT_LONG_10M_PRECISION, LAT_LONG_1CM_PRECISION, LAT_LONG_1KM_PRECISION, LAT_LONG_1MM_PRECISION, LAT_LONG_1M_PRECISION, LAT_LONG_GRAINS_OF_SAND_PRECISION, LEADING_SLASHES_REGEX, MAP_IDENTITY, MARKDOWN_MIME_TYPE, MAX_BITWISE_SET_SIZE, MAX_LATITUDE_VALUE, MAX_LONGITUDE_VALUE, MINUTES_IN_DAY, MINUTES_IN_HOUR, MINUTE_OF_DAY_MAXMIMUM, MINUTE_OF_DAY_MINIUMUM, MIN_LATITUDE_VALUE, MIN_LONGITUDE_VALUE, MONTH_DAY_SLASH_DATE_STRING_REGEX, MS_IN_DAY, MS_IN_HOUR, MS_IN_MINUTE, MS_IN_SECOND, MS_IN_WEEK, MemoryStorageInstance, ModelRelationUtility, NOOP_MODIFIER, NUMBER_STRING_DENCODER_64, NUMBER_STRING_DENCODER_64_DIGITS, OAUTH_OOB_REDIRECT_URI, PDF_ENCRYPT_MARKER, PDF_EOF_MARKER, PDF_HEADER, PDF_MIME_TYPE, PHONE_EXTENSION_NUMBER_REGEX, PJPEG_MIME_TYPE, PNG_MIME_TYPE, PRIMATIVE_KEY_DENCODER_VALUE, PropertyDescriptorUtility, RAW_MIME_TYPE, REGEX_SPECIAL_CHARACTERS, REGEX_SPECIAL_CHARACTERS_SET, RelationChange, SECONDS_IN_DAY, SECONDS_IN_HOUR, SECONDS_IN_MINUTE, SHARED_MEMORY_STORAGE, SLASH_PATH_FILE_TYPE_SEPARATOR, SLASH_PATH_SEPARATOR, SORT_VALUE_EQUAL, SORT_VALUE_GREATER_THAN, SORT_VALUE_LESS_THAN, SPACE_JOINER, SPACE_STRING_SPLIT_JOIN, SPLIT_STRING_TREE_NODE_ROOT_VALUE, SVG_MIME_TYPE, ServerErrorResponse, SetDeltaChange, SimpleStorageObject, SlashPathPathMatcherPartCode, StorageObject, StorageObjectUtility, StoredDataError, SyncState, TIFF_MIME_TYPE, TIME_UNIT_LABEL_MAP, TIME_UNIT_SHORT_LABEL_MAP, TOTAL_LATITUDE_RANGE, TOTAL_LONGITUDE_RANGE, TOTAL_SPAN_OF_LONGITUDE, TRAILING_FILE_TYPE_SEPARATORS_REGEX, TRAILING_SLASHES_REGEX, TXT_MIME_TYPE, TimeAM, TimerCancelledError, TypedServiceRegistryInstance, UNLOADED_PAGE, UNSET_INDEX_NUMBER, US_STATE_CODE_STRING_REGEX, UTC_DATE_STRING_REGEX, UTC_TIMEZONE_STRING, UTF_8_START_CHARACTER, UTF_PRIVATE_USAGE_AREA_START, UnauthorizedServerErrorResponse, WEBP_MIME_TYPE, WEBSITE_TLD_DETECTION_REGEX, WEB_PROTOCOL_PREFIX_REGEX, XLSX_MIME_TYPE, XML_MIME_TYPE, YAML_MIME_TYPE, ZIP_CODE_STRING_REGEX, ZIP_FILE_MIME_TYPE, addHttpToUrl, addLatLngPoints, addMilliseconds, addModifiers, addPlusPrefixToNumber, addPrefix, addPrefixFunction, addSuffix, addSuffixFunction, addToSet, addToSetCopy, addToSplitStringTree, addTrailingSlash, allFalsyOrEmptyKeys, allIndexesInIndexRange, allKeyValueTuples, allMaybeSoKeys, allNonUndefinedKeys, allObjectsAreEqual, allValuesAreMaybeNot, allValuesAreNotMaybe, allowValueOnceFilter, applicationFileExtensionForMimeType, applyBestFit, applySplitStringTreeWithMultipleValues, applyToMultipleFields, approximateTimerEndDate, areEqualContext, areEqualPOJOValues, areEqualPOJOValuesUsingPojoFilter, arrayContainsDuplicateValue, arrayContentsDiffer, arrayDecision, arrayDecisionFunction, arrayFactory, arrayInputFactory, arrayToLowercase, arrayToMap, arrayToObject, arrayToUppercase, asArray, asDecisionFunction, asGetter, asIndexRangeCheckFunctionConfig, asIterable, asMinuteOfDay, asNonEmptyArray, asNumber, asObjectCopyFactory, asPromise, asSet, assignValuesToPOJO, assignValuesToPOJOFunction, authClaims, authRoleClaimsService, authRolesSetHasRoles, baseWebsiteUrl, batch, batchCalc, bitwiseObjectDencoder, bitwiseObjectEncoder, bitwiseObjectdecoder, bitwiseSetDecoder, bitwiseSetDencoder, booleanFactory, booleanKeyArrayUtility, boundNumber, boundNumberFunction, boundToRectangle, breadthFirstExploreTreeTraversalFactoryFunction, bufferHasValidPdfMarkings, build, buildCanonicalFilename, cachedGetter, calculateExpirationDate, camelOrPascalToScreamingSnake, capLatValue, capitalizeFirstLetter, caseInsensitiveFilterByIndexOfDecisionFactory, caseInsensitiveString, catchAllHandlerKey, chainMapFunction, chainMapSameFunctions, characterPrefixSuffixInstance, checkAnyHaveExpired, checkAtleastOneNotExpired, classifySpecFile, coerceToEmailParticipants, combineMaps, compareEqualityWithValueFromItemsFunction, compareEqualityWithValueFromItemsFunctionFactory, compareFnOrder, compareStrings, compareStringsNumeric, compareWithMappedValuesFunction, computeNextFractionalHour, computeNextFreeIndexFunction, computeNextFreeIndexOnSortedValuesFunction, concatArrays, concatArraysUnique, containsAllStringsAnyCase, containsAllValues, containsAnyStringAnyCase, containsAnyValue, containsAnyValueFromSet, containsNoValueFromSet, containsNoneOfValue, containsStringAnyCase, convertEmailParticipantStringToParticipant, convertMaybeToArray, convertMaybeToNonEmptyArray, convertParticipantToEmailParticipantString, convertTimeDuration, convertToArray, copyArray, copyField, copyLatLngBound, copyLatLngPoint, copyObject, copySetAndDo, countAllInNestedArray, countPOJOKeys, countPOJOKeysFunction, cronExpressionRepeatingEveryNMinutes, cssClassesSet, cssTokenVar, cssVariableVar, cutString, cutStringFunction, cutToPrecision, cutValueToInteger, cutValueToPrecision, cutValueToPrecisionFunction, dateFromDateOrTimeMillisecondsNumber, dateFromDateOrTimeSecondsNumber, dateFromLogicalDate, dateFromMinuteOfDay, dateOrMillisecondsToDate, dateToHoursAndMinutes, dateToMinuteOfDay, dayOfWeek, daysOfWeekArray, daysOfWeekFromEnabledDays, daysOfWeekNameFunction, daysOfWeekNameMap, decisionFunction, decodeHashedValues, decodeHashedValuesWithDecodeMap, decodeModelKeyTypePair, decodeRadix36Number, defaultFilterFromPOJOFunctionNoCopy, defaultForwardFunctionFactory, defaultLatLngPoint, defaultLatLngString, dencodeBitwiseSet, depthFirstExploreTreeTraversalFactoryFunction, diffLatLngBoundPoints, diffLatLngPoints, documentFileExtensionForMimeType, dollarAmountString, dollarAmountStringWithUnitFunction, e164PhoneNumberExtensionPair, e164PhoneNumberFromE164PhoneNumberExtensionPair, enabledDaysFromDaysOfWeek, encodeBitwiseSet, encodeModelKeyTypePair, encodeRadix36Number, errorMessageContainsString, errorMessageContainsStringFunction, escapeStringCharactersFunction, escapeStringForRegex, excludeValues, excludeValuesFromArray, excludeValuesFromSet, existsInIterable, expandArrayMapTuples, expandArrayValueTuples, expandFlattenTreeFunction, expandIndexSet, expandSlashPathPathMatcherPartToDecisionFunctions, expandTreeFunction, expandTrees, expirationDetails, exploreTreeFunction, exponentialPromiseRateLimiter, extendLatLngBound, fileExtensionForMimeType, filterAndMapFunction, filterEmptyArrayValues, filterEmptyPojoValues, filterFalsyAndEmptyValues, filterFromIterable, filterFromPOJO, filterFromPOJOFunction, filterKeyValueTupleFunction, filterKeyValueTuples, filterKeyValueTuplesFunction, filterKeyValueTuplesInputToFilter, filterKeysOnPOJOFunction, filterMaybeArrayFunction, filterMaybeArrayValues, filterNullAndUndefinedValues, filterOnlyUndefinedValues, filterTuplesOnPOJOFunction, filterUndefinedValues, filterUniqueByIndex, filterUniqueCaseInsensitiveStrings, filterUniqueFunction, filterUniqueTransform, filterUniqueValues, filterValuesByDistance, filterValuesByDistanceNoOrder, filterValuesToSet, filterValuesUsingSet, filteredPage, findAllCharacterOccurences, findAllCharacterOccurencesFunction, findBest, findBestIndexMatch, findBestIndexMatchFunction, findBestIndexSetPair, findBestSplitStringTreeChildMatch, findBestSplitStringTreeChildMatchPath, findBestSplitStringTreeMatch, findBestSplitStringTreeMatchPath, findFirstCharacterOccurence, findInIterable, findIndexOfFirstDuplicateValue, findItemsByIndex, findNext, findPOJOKeys, findPOJOKeysFunction, findStringsRegexString, findToIndexSet, findValuesFrom, firstAndLastCharacterOccurrence, firstAndLastValue, firstValue, firstValueFromIterable, fitToIndexRangeFunction, fixExtraQueryParameters, fixMultiSlashesInSlashPath, flattenArray, flattenArrayOrValueArray, flattenArrayToSet, flattenArrayUnique, flattenArrayUniqueCaseInsensitiveStrings, flattenObject, flattenTree, flattenTreeToArray, flattenTreeToArrayFunction, flattenWhitespace, forEachInIterable, forEachKeyValue, forEachKeyValueOnPOJOFunction, forEachWithArray, forwardFunction, fractionalHoursToMinutes, generateIfDoesNotExist, generatePkceCodeChallenge, generatePkceCodeVerifier, getArrayNextIndex, getBaseLog, getDayOffset, getDayTomorrow, getDayYesterday, getDaysOfWeekNames, getFunctionType, getNextDay, getNextPageNumber, getOverlappingRectangle, getPageNumber, getPreviousDay, getValueFromGetter, groupValues, handlerBindAccessor, handlerConfigurerFactory, handlerFactory, handlerMappedSetFunction, handlerMappedSetFunctionFactory, handlerSetFunction, hasDifferentStringsNoCase, hasDifferentValues, hasHttpPrefix, hasNonNullValue, hasPortNumber, hasSameTimezone, hasSameValues, hasValueFunction, hasValueOrNotEmpty, hasValueOrNotEmptyObject, hasWebsiteDomain, hasWebsiteTopLevelDomain, hashSetForIndexed, hourToFractionalHour, hoursAndMinutesToString, hoursAndMinutesToTimeUnit, idBatchFactory, imageFileExtensionForMimeType, inMemoryAsyncKeyedValueCache, inMemoryAsyncValueCache, incrementingNumberFactory, indexDeltaGroup, indexDeltaGroupFunction, indexRange, indexRangeCheckFunction, indexRangeCheckReaderFunction, indexRangeForArray, indexRangeOverlapsIndexRange, indexRangeOverlapsIndexRangeFunction, indexRangeReaderPairFactory, indexRefMap, indexedValuesArrayAccessorFactory, insertIntoBooleanKeyArray, invertBooleanReturnFunction, invertDecision, invertFilter, invertMaybeBoolean, invertStringRecord, isAllowed, isClassLikeType, isCompleteUnitedStatesAddress, isConsideredUtcTimezoneString, isDate, isDefaultLatLngPoint, isDefaultLatLngPointValue, isDefaultReadableError, isDefinedAndNotFalse, isDollarAmountString, isE164PhoneNumber, isE164PhoneNumberWithExtension, isEmptyIterable, isEqualContext, isEqualDate, isEqualToValueDecisionFunction, isEvenNumber, isExpired, isFalseBooleanKeyArray, isFinalPage, isGetter, isHex, isHexWithByteLength, isHourOfDay, isISO8601DateString, isISO8601DayString, isISO8601DayStringStart, isInAllowedDaysOfWeekSet, isInNumberBoundFunction, isInSetDecisionFunction, isIndexNumberInIndexRange, isIndexNumberInIndexRangeFunction, isIndexRangeInIndexRange, isIndexRangeInIndexRangeFunction, isIterable, isKnownHttpWebsiteProtocol, isLatLngBound, isLatLngBoundWithinLatLngBound, isLatLngPoint, isLatLngPointWithinLatLngBound, isLatLngString, isLogicalDateStringCode, isMapIdentityFunction, isMaybeNot, isMaybeNotOrTrue, isMaybeSo, isMinuteOfDay, isModelKey, isMonthDaySlashDate, isNonClassFunction, isNotFalse, isNotNullOrEmptyString, isNumberDivisibleBy, isObjectWithConstructor, isOddNumber, isPast, isPdfPasswordProtected, isPromise, isPromiseLike, isSameLatLngBound, isSameLatLngPoint, isSameNonNullValue, isSameVector, isSelectedDecisionFunctionFactory, isSelectedIndexDecisionFunction, isServerError, isSlashPathFile, isSlashPathFolder, isSlashPathTypedFile, isStandardInternetAccessibleWebsiteUrl, isStringOrTrue, isThrottled, isTrueBooleanKeyArray, isUTCDateString, isUnderThreshold, isUniqueKeyedFunction, isUsStateCodeString, isValidLatLngPoint, isValidLatitude, isValidLongitude, isValidNumberBound, isValidPhoneExtensionNumber, isValidSlashPath, isWebsiteUrl, isWebsiteUrlWithPrefix, isWithinLatLngBoundFunction, isolateSlashPath, isolateSlashPathFunction, isolateWebsitePathFunction, itemCountForBatchIndex, iterableToArray, iterableToMap, iterableToSet, iterablesAreSetEquivalent, iterate, iterateFilteredPages, joinHostAndPort, joinStrings, joinStringsInstance, joinStringsWithCommas, joinStringsWithSpaces, keepCharactersAfterFirstCharacterOccurence, keepCharactersAfterFirstCharacterOccurenceFunction, keepFromSetCopy, keepValuesFromArray, keepValuesFromSet, keyValueMapFactory, labeledValueMap, lastValue, latLngBound, latLngBoundCenterPoint, latLngBoundEastBound, latLngBoundFromInput, latLngBoundFullyWrapsMap, latLngBoundFunction, latLngBoundNorthBound, latLngBoundNorthEastPoint, latLngBoundNorthWestPoint, latLngBoundOverlapsLatLngBound, latLngBoundSouthBound, latLngBoundSouthEastPoint, latLngBoundSouthWestPoint, latLngBoundStrictlyWrapsMap, latLngBoundTuple, latLngBoundTupleFunction, latLngBoundWestBound, latLngBoundWrapsMap, latLngDataPointFunction, latLngPoint, latLngPointFromString, latLngPointFunction, latLngPointPrecisionFunction, latLngString, latLngStringFunction, latLngTuple, latLngTupleFunction, limitArray, lonLatTuple, lowercaseFirstLetter, mailToUrlString, makeBestFit, makeCopyModelFieldFunction, makeDateMonthForMonthOfYear, makeDefaultNonConcurrentTaskKeyFactory, makeGetter, makeHandler, makeHashDecodeMap, makeKeyPairs, makeModelConversionFieldValuesFunction, makeModelMap, makeModelMapFunctions, makeMultiModelKeyMap, makeTimer, makeValuesGroupMap, makeWithFactory, makeWithFactoryInput, mapArrayFunction, mapFunctionOutput, mapFunctionOutputPair, mapGetter, mapGetterFactory, mapIdentityFunction, mapIterable, mapKeysIntersectionObjectToArray, mapMaybeFunction, mapObjectKeysFunction, mapObjectKeysToLowercase, mapObjectMap, mapObjectMapFunction, mapObjectToTargetObject, mapPromiseOrValue, mapToObject, mapToTuples, mapValuesToSet, mappedUseAsyncFunction, mappedUseFunction, mapsHaveSameKeys, maybeMergeModelModifiers, maybeMergeModifiers, maybeModifierMapToFunction, maybeSet, memoizeAsyncKeyedValueCache, memoizeAsyncValueCache, mergeArrays, mergeArraysIntoArray, mergeAsyncKeyedValueCaches, mergeAsyncValueCaches, mergeFilterFunctions, mergeModifiers, mergeObjects, mergeObjectsFunction, mergeSlashPaths, messageFromError, millisecondsToMinutes, millisecondsToMinutesAndSeconds, millisecondsToTimeUnit, mimeTypeForApplicationFileExtension, mimeTypeForDocumentFileExtension, mimeTypeForFileExtension, mimeTypeForImageFileExtension, minAndMaxFunction, minAndMaxIndex, minAndMaxIndexFunction, minAndMaxIndexItemsFunction, minAndMaxNumber, minutesToFractionalHours, minutesToHoursAndMinutes, modelFieldConversions, modelFieldMapFunction, modelFieldMapFunctions, modelTypeDataPairFactory, modifier, modifierMapToFunction, modifyModelMapFunction, modifyModelMapFunctions, monthDaySlashDateToDateString, monthOfYearFromDate, monthOfYearFromDateMonth, monthOfYearFromUTCDate, multiKeyValueMapFactory, multiValueMapBuilder, neMostLatLngPoint, nearestDivisibleValues, noop, numberStringDencoder, numberStringDencoderDecodedNumberValueFunction, numberStringDencoderEncodedStringValueFunction, numberStringDencoderFunction, objectCopyFactory, objectDeltaArrayCompressor, objectFieldEqualityChecker, objectFlatMergeMatrix, objectHasKey, objectHasKeys, objectHasNoKeys, objectIsEmpty, objectKeyEqualityComparatorFunction, objectKeysEqualityComparatorFunction, objectMergeMatrix, objectToMap, overlapsLatLngBoundFunction, overrideInObject, overrideInObjectFunctionFactory, padStartFunction, pairGroupValues, parseISO8601DayStringToUTCDate, partialServerError, MAP_IDENTITY as passThrough, percentNumberFromDecimal, percentNumberToDecimal, performAsyncTask, performAsyncTasks, performBatchLoop, performMakeLoop, performTaskCountLoop, performTaskLoop, performTasksFromFactoryInParallelFunction, performTasksInParallel, performTasksInParallelFunction, pickOneRandomly, poll, primativeKeyDencoder, primativeKeyDencoderMap, primativeKeyStringDencoder, primativeValuesDelta, promiseReference, protectedFactory, pushArrayItemsIntoArray, pushElementOntoArray, pushItemOrArrayItemsIntoArray, randomArrayFactory, randomArrayIndex, randomBoolean, randomEmailFactory, randomFromArrayFactory, randomLatLngFactory, randomLatLngFromCenterFactory, randomNumber, randomNumberFactory, randomPhoneNumberFactory, randomPickFactory, range, rangedIndexedValuesArrayAccessorFactory, rangedIndexedValuesArrayAccessorInfoFactory, readBooleanKeySafetyWrap, readDomainFromEmailAddress, readDomainsFromEmailAddresses, readEmailDomainFromUrlOrEmailAddress, readIndexNumber, readKeysFrom, readKeysFromFilterUniqueFunctionAdditionalKeys, readKeysFromFilterUniqueFunctionAdditionalKeysInput, readKeysFunction, readKeysSetFrom, readKeysSetFunction, readKeysToMap, readModelKey, readModelKeyFromObject, readModelKeys, readModelKeysFromObjects, readMultipleKeysToMap, readPortNumber, readUniqueModelKey, readWebsiteProtocol, readableError, readableStreamToBase64, readableStreamToBuffer, readableStreamToStringFunction, recommendBucketsForGroup, recommendSpecPath, rectangleOverlapsRectangle, reduceBooleansFn, reduceBooleansWithAnd, reduceBooleansWithAndFn, reduceBooleansWithOr, reduceBooleansWithOrFn, reduceNumbers, reduceNumbersFn, reduceNumbersWithAdd, reduceNumbersWithAddFn, reduceNumbersWithMax, reduceNumbersWithMaxFn, reduceNumbersWithMin, reduceNumbersWithMinFn, removeByKeyFromBooleanKeyArray, removeCharactersAfterFirstCharacterOccurence, removeCharactersAfterFirstCharacterOccurenceFunction, removeExtensionFromPhoneNumber, removeFirstMatchingSuffix, removeFromBooleanKeyArray, removeFromSet, removeFromSetCopy, removeHttpFromUrl, removeModelsWithKey, removeModelsWithSameKey, removeModifiers, removeSuffix, removeTrailingFileTypeSeparators, removeTrailingSlashes, removeValuesAtIndexesFromArrayCopy, removeWebProtocolPrefix, repeatString, replaceCharacterAtIndexIf, replaceCharacterAtIndexWith, replaceInvalidFilePathTypeSeparatorsInSlashPath, replaceInvalidFilePathTypeSeparatorsInSlashPathFunction, replaceLastCharacterIf, replaceLastCharacterIfIsFunction, replaceMultipleFilePathsInSlashPath, replaceStringsFunction, requireModelKey, resetPeriodPromiseRateLimiter, restoreOrder, restoreOrderWithValues, reverseCompareFn, roundNumberToStepFunction, roundNumberUpToStep, roundToPrecision, roundToPrecisionFunction, roundingFunction, runAsyncTaskForValue, runAsyncTasksForValues, runNamedAsyncTasks, runNamedAsyncTasksFunction, safeCompareEquality, safeEqualityComparatorFunction, safeFindBestIndexMatch, screamingSnakeToCamelCase, searchStringFilterFunction, secondsToMinutesAndSeconds, selectiveFieldEncryptor, separateValues, separateValuesToSets, sequentialIncrementingNumberStringModelIdFactory, serverError, setContainsAllValues, setContainsAnyValue, setContainsNoneOfValue, setDeltaChangeKeys, setDeltaFunction, setHasValueFunction, setIncludes, setIncludesFunction, setKeysOnMap, setWebProtocolPrefix, setsAreEquivalent, simpleSortValuesFunctionWithSortRef, simplifyWhitespace, slashPathDetails, slashPathDirectoryTree, slashPathFactory, slashPathFolder, slashPathFolderFactory, slashPathInvalidError, slashPathName, slashPathParts, slashPathPathMatcher, slashPathPathMatcherConfig, slashPathStartTypeFactory, slashPathSubPathMatcher, slashPathType, slashPathValidationFactory, sliceIndexRangeFunction, sliceStringFunction, sortAscendingIndexNumberRefFunction, sortByIndexAscendingCompareFunction, sortByIndexRangeAscendingCompareFunction, sortByLabelFunction, sortByNumberFunction, sortByStringFunction, sortCompareNumberFunction, sortNumbersAscendingFunction, sortValues, sortValuesFunctionOrMapIdentityWithSortRef, sortValuesFunctionWithSortRef, spaceSeparatedCssClasses, splitCommaSeparatedString, splitCommaSeparatedStringToSet, splitFront, splitJoinNameString, splitJoinRemainder, splitStringAtFirstCharacterOccurence, splitStringAtFirstCharacterOccurenceFunction, splitStringAtIndex, splitStringTreeFactory, startOfDayForSystemDateInUTC, startOfDayForUTCDateInUTC, stepsFromIndex, stepsFromIndexFunction, stringCharactersToIndexRecord, stringContains, stringFactoryFromFactory, stringFromDateFactory, stringFromTimeFactory, stringSplitJoinInstance, stringToBoolean, stringToLowercaseFunction, stringToUppercaseFunction, stringTrimFunction, stripObject, stripObjectFunction, sumOfIntegersBetween, swMostLatLngPoint, symmetricDifferenceArray, symmetricDifferenceArrayBetweenSets, symmetricDifferenceWithModels, takeFront, takeLast, takeValuesFromIterable, telUrlString, telUrlStringForE164PhoneNumberPair, terminatingFactoryFromArray, throwKeyIsRequired, timeDurationToHoursAndMinutes, timeDurationToMilliseconds, timePeriodCounter, timeUnitToMilliseconds, toAbsoluteSlashPathStartType, toCaseInsensitiveStringArray, toMinuteOfDay, toModelFieldConversions, toModelMapFunctions, toReadableError, toRelativeSlashPathStartType, toggleInSet, toggleInSetCopy, toggleTimerRunning, transformNumberFunction, transformNumberFunctionConfig, transformStringFunction, transformStringFunctionConfig, transformStrings, trimArray, trueOrFalseString, tryConvertToE164PhoneNumber, tryWithPromiseFactoriesFunction, typedServiceRegistry, unique, uniqueCaseInsensitiveStrings, uniqueCaseInsensitiveStringsSet, uniqueKeys, uniqueModels, unitedStatesAddressString, unixDateTimeSecondsNumberForNow, unixDateTimeSecondsNumberFromDate, unixDateTimeSecondsNumberFromDateOrTimeNumber, unixDateTimeSecondsNumberToDate, unixMillisecondsNumberToDate, updateMaybeValue, urlWithoutParameters, useAsync, useCallback, useContextFunction, useIterableOrValue, useModelOrKey, usePromise, useValue, validLatLngPoint, validLatLngPointFunction, valueAtIndex, valuesAreBothNullishOrEquivalent, valuesFromPOJO, valuesFromPOJOFunction, vectorMinimumSizeResizeFunction, vectorsAreEqual, waitForMs, websiteDomainAndPathPair, websiteDomainAndPathPairFromWebsiteUrl, websitePathAndQueryPair, websitePathFromWebsiteDomainAndPath, websitePathFromWebsiteUrl, websiteUrlDetails, websiteUrlFromPaths, wrapIndexRangeFunction, wrapLatLngPoint, wrapLngValue, wrapMapFunctionOutput, wrapNumberFunction, wrapTuples, wrapUseAsyncFunction, wrapUseFunction };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/util",
3
- "version": "13.12.3",
3
+ "version": "13.12.5",
4
4
  "sideEffects": false,
5
5
  "exports": {
6
6
  "./test": {
@@ -35,3 +35,4 @@ export * from './path';
35
35
  export * from './sort';
36
36
  export * from './type';
37
37
  export * from './encryption';
38
+ export * from './test';
@@ -0,0 +1 @@
1
+ export * from './spec-file-conventions';
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Spec-file naming conventions for API test files.
3
+ *
4
+ * Downstream Firebase Functions API apps keep their tests under
5
+ * `<apiDir>/src/app/function/<group>/`, with each spec file named so that
6
+ * "what does this test, and where do I add the next one?" can be answered
7
+ * from the filename alone. The canonical forms are:
8
+ *
9
+ * - `<group>.crud.spec.ts` — non-scenario tests of the CRUD function map for the group.
10
+ * - `<group>.crud.<sub>[.<sub>...].spec.ts` — CRUD tests focused on a sub-area
11
+ * (e.g. `job.crud.requirement.spec.ts`).
12
+ * - `<group>.scenario.spec.ts` — generic multi-step scenario tests using fixture
13
+ * chains.
14
+ * - `<group>.scenario.<sub>[.<sub>...].spec.ts` — focused scenario sub-bucket
15
+ * (e.g. `job.scenario.requirement.worker.state.spec.ts`).
16
+ *
17
+ * Drift forms (still parsed, but flagged with a rename suggestion):
18
+ * - `<group>.<sub>.crud.spec.ts` — `crud` placed after a subgroup.
19
+ * Suggestion: `<group>.crud.<sub>.spec.ts`.
20
+ * - `<group>.<sub>.scenario.spec.ts` — `scenario` placed after a subgroup
21
+ * (e.g. `worker.payroll.scenario.spec.ts`, `school.job.publish.scenario.spec.ts`).
22
+ * Suggestion: `<group>.scenario.<sub>.spec.ts`.
23
+ * - `<group>.<rest>.spec.ts` — no `crud` / `scenario` segment at all
24
+ * (e.g. `worker.system.spec.ts`). Defaults to a scenario rename suggestion
25
+ * because the missing bucket is more often scenario-like.
26
+ *
27
+ * This module is pure: classification and canonical-path rendering live
28
+ * here, with no disk I/O. It's consumed by `@dereekb/dbx-components-mcp`
29
+ * (the `dbx_model_test_*` tool cluster) and by
30
+ * `@dereekb/firebase/eslint` (the `require-canonical-api-spec-filename`
31
+ * and `require-api-crud-spec-for-group` rules).
32
+ */
33
+ /**
34
+ * The naming buckets recognised by the classifier. `non-spec` is emitted when
35
+ * the input does not end in `.spec.ts`; `non-group` when the first filename
36
+ * segment doesn't match the parent folder name (the file belongs to a different
37
+ * model group or to no group at all).
38
+ */
39
+ export type SpecFileKind = 'crud' | 'crud-subgroup' | 'scenario' | 'scenario-subgroup' | 'crud-misplaced' | 'scenario-misplaced' | 'no-bucket' | 'non-spec' | 'non-group';
40
+ /**
41
+ * Whether a given kind is on-convention. Drift kinds (`-misplaced`,
42
+ * `no-bucket`) are still parseable but earn a warning + rename suggestion.
43
+ */
44
+ export declare const CANONICAL_KINDS: readonly SpecFileKind[];
45
+ /**
46
+ * One classified spec file. Drift entries carry a {@link recommendedRename}
47
+ * suggesting the canonical filename; canonical entries leave it undefined.
48
+ */
49
+ export interface SpecFileClassification {
50
+ readonly filename: string;
51
+ readonly group: string;
52
+ readonly kind: SpecFileKind;
53
+ /**
54
+ * Segments between the group name and the `crud` / `scenario` marker for
55
+ * `crud-subgroup` / `scenario-subgroup` kinds (e.g. `['requirement','worker']`
56
+ * for `job.scenario.requirement.worker.spec.ts`). Empty for `crud` /
57
+ * `scenario` and for non-group kinds.
58
+ */
59
+ readonly subgroups: readonly string[];
60
+ /**
61
+ * `true` when the kind is one of {@link CANONICAL_KINDS}.
62
+ */
63
+ readonly isCanonical: boolean;
64
+ /**
65
+ * Suggested canonical filename when the input is drift. `undefined` for
66
+ * canonical entries and for {@link `non-spec`} / {@link `non-group`}.
67
+ */
68
+ readonly recommendedRename?: string;
69
+ /**
70
+ * Human-readable reason emitted with drift kinds; `undefined` for canonical
71
+ * entries. Used by the formatter so each warning line explains itself.
72
+ */
73
+ readonly driftReason?: string;
74
+ }
75
+ /**
76
+ * Classifies a spec filename against the conventions for the given parent
77
+ * folder. Pure function — never touches the filesystem.
78
+ *
79
+ * @param config - Inputs.
80
+ * @param config.filename - Bare filename including the `.spec.ts` suffix
81
+ * (e.g. `job.scenario.requirement.spec.ts`).
82
+ * @param config.parentFolderName - Name of the directory containing the file
83
+ * (e.g. `job`). Used to detect cross-group misplacement.
84
+ * @returns The classification.
85
+ */
86
+ export declare function classifySpecFile(config: {
87
+ readonly filename: string;
88
+ readonly parentFolderName: string;
89
+ }): SpecFileClassification;
90
+ /**
91
+ * Renders a canonical spec filename for the given group + bucket + subgroup
92
+ * chain. Pure data — used both by drift remediation and by the
93
+ * `recommendSpecPath()` helper below.
94
+ *
95
+ * @param config - Inputs.
96
+ * @param config.group - The model-group name (e.g. `job`).
97
+ * @param config.bucket - `crud` or `scenario`.
98
+ * @param config.subgroups - Optional ordered subgroup segments
99
+ * (e.g. `['requirement','worker']`).
100
+ * @returns The canonical filename (e.g. `job.scenario.requirement.worker.spec.ts`).
101
+ */
102
+ export declare function buildCanonicalFilename(config: {
103
+ readonly group: string;
104
+ readonly bucket: 'crud' | 'scenario';
105
+ readonly subgroups: readonly string[];
106
+ }): string;
107
+ /**
108
+ * Renders the canonical relative path for a spec file under a given API app.
109
+ * The shape mirrors the layout used throughout the workspace:
110
+ * `<apiDir>/src/app/function/<group>/<filename>`.
111
+ *
112
+ * @param config - Inputs.
113
+ * @param config.apiDir - The relative API-app directory
114
+ * (e.g. `apps/hellosubs-api`).
115
+ * @param config.group - The model-group name.
116
+ * @param config.bucket - `crud` or `scenario`.
117
+ * @param config.subgroups - Optional subgroup chain.
118
+ * @returns The canonical relative spec-file path.
119
+ */
120
+ export declare function recommendSpecPath(config: {
121
+ readonly apiDir: string;
122
+ readonly group: string;
123
+ readonly bucket: 'crud' | 'scenario';
124
+ readonly subgroups: readonly string[];
125
+ }): string;
126
+ /**
127
+ * One recommendation slot rendered by the list-app tool's "where to add a new
128
+ * test" section. `subgroups` is left empty for the base buckets; callers
129
+ * substitute their own subgroup chain when scaffolding a focused file.
130
+ */
131
+ export interface SpecBucketRecommendation {
132
+ readonly bucket: 'crud' | 'scenario';
133
+ readonly label: string;
134
+ readonly canonicalPath: string;
135
+ readonly summary: string;
136
+ }
137
+ /**
138
+ * Returns the two canonical buckets for a given model group, with rendered
139
+ * paths and one-line summaries. Used by the list-app formatter.
140
+ *
141
+ * @param config - Inputs.
142
+ * @param config.apiDir - Relative API-app directory.
143
+ * @param config.group - Model-group name.
144
+ * @returns The crud + scenario recommendations.
145
+ */
146
+ export declare function recommendBucketsForGroup(config: {
147
+ readonly apiDir: string;
148
+ readonly group: string;
149
+ }): readonly SpecBucketRecommendation[];
package/test/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@dereekb/util/test",
3
- "version": "13.12.3",
3
+ "version": "13.12.5",
4
4
  "peerDependencies": {
5
- "@dereekb/util": "13.12.3",
5
+ "@dereekb/util": "13.12.5",
6
6
  "make-error": "^1.3.6"
7
7
  },
8
8
  "exports": {