@dereekb/firebase 13.36.0 → 13.38.0

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.
@@ -18076,6 +18076,500 @@ function _unsupported_iterable_to_array(o, minLen) {
18076
18076
  }
18077
18077
  };
18078
18078
 
18079
+ /**
18080
+ * Name of the `@dereekb/model` helper that expands an arktype definition to `T | null | undefined`.
18081
+ */ var CLEARABLE_FUNCTION_NAME = 'clearable';
18082
+ /**
18083
+ * Module that publishes {@link CLEARABLE_FUNCTION_NAME}.
18084
+ */ var CLEARABLE_IMPORT_MODULE = '@dereekb/model';
18085
+ /**
18086
+ * Identifier callees whose object-literal argument is an arktype definition (`type({ ... })`, `scope({ ... })`).
18087
+ */ var DEFAULT_ARKTYPE_DEFINITION_CALLEE_NAMES = [
18088
+ 'type',
18089
+ 'scope'
18090
+ ];
18091
+ /**
18092
+ * Arktype combinator methods that take an object-literal definition (`targetModelParamsType.merge({ ... })`).
18093
+ */ var DEFAULT_ARKTYPE_COMBINATOR_METHOD_NAMES = [
18094
+ 'merge',
18095
+ 'and',
18096
+ 'or',
18097
+ 'extend'
18098
+ ];
18099
+ /**
18100
+ * Method used to union an existing Type with another definition (`someType.or('null | undefined')`).
18101
+ */ var OR_METHOD_NAME = 'or';
18102
+ /**
18103
+ * The nullish arktype keywords {@link CLEARABLE_FUNCTION_NAME} appends to its definition.
18104
+ */ var NULLISH_KEYWORDS = new Set([
18105
+ 'null',
18106
+ 'undefined'
18107
+ ]);
18108
+ /**
18109
+ * Splits an arktype definition into its top-level `|` members, ignoring separators nested inside
18110
+ * quotes, parentheses, brackets, or braces (`${...}` template holes included).
18111
+ *
18112
+ * @param text - The definition text, without its surrounding quote/backtick delimiters.
18113
+ * @returns The union members in source order.
18114
+ */ function splitTopLevelUnion(text) {
18115
+ var members = [];
18116
+ var depth = 0;
18117
+ var quote = null;
18118
+ var start = 0;
18119
+ for(var i = 0; i < text.length; i += 1){
18120
+ var char = text[i];
18121
+ if (quote != null) {
18122
+ if (char === '\\') {
18123
+ i += 1;
18124
+ } else if (char === quote) {
18125
+ quote = null;
18126
+ }
18127
+ } else if (char === "'" || char === '"') {
18128
+ quote = char;
18129
+ } else if (char === '(' || char === '[' || char === '{') {
18130
+ depth += 1;
18131
+ } else if (char === ')' || char === ']' || char === '}') {
18132
+ depth -= 1;
18133
+ } else if (char === '|' && depth === 0) {
18134
+ members.push({
18135
+ text: text.slice(start, i),
18136
+ start: start
18137
+ });
18138
+ start = i + 1;
18139
+ }
18140
+ }
18141
+ members.push({
18142
+ text: text.slice(start),
18143
+ start: start
18144
+ });
18145
+ return members;
18146
+ }
18147
+ /**
18148
+ * Inspects an arktype definition for top-level `null` / `undefined` union members.
18149
+ *
18150
+ * @param text - The definition text, without its surrounding quote/backtick delimiters.
18151
+ * @returns The nullish members found and the definition text with a trailing nullish run removed.
18152
+ */ function splitNullishUnion(text) {
18153
+ var members = splitTopLevelUnion(text);
18154
+ var hasNull = false;
18155
+ var hasUndefined = false;
18156
+ var nullishIsSuffix = true;
18157
+ var firstNullishStart = null;
18158
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
18159
+ try {
18160
+ for(var _iterator = members[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
18161
+ var member = _step.value;
18162
+ var trimmed = member.text.trim();
18163
+ if (NULLISH_KEYWORDS.has(trimmed)) {
18164
+ hasNull = hasNull || trimmed === 'null';
18165
+ hasUndefined = hasUndefined || trimmed === 'undefined';
18166
+ if (firstNullishStart == null) {
18167
+ firstNullishStart = member.start;
18168
+ }
18169
+ } else if (firstNullishStart != null) {
18170
+ nullishIsSuffix = false;
18171
+ }
18172
+ }
18173
+ } catch (err) {
18174
+ _didIteratorError = true;
18175
+ _iteratorError = err;
18176
+ } finally{
18177
+ try {
18178
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
18179
+ _iterator.return();
18180
+ }
18181
+ } finally{
18182
+ if (_didIteratorError) {
18183
+ throw _iteratorError;
18184
+ }
18185
+ }
18186
+ }
18187
+ var base = firstNullishStart == null ? text : text.slice(0, firstNullishStart).replace(/[\s|]+$/, '');
18188
+ return {
18189
+ base: base,
18190
+ hasNull: hasNull,
18191
+ hasUndefined: hasUndefined,
18192
+ nullishIsSuffix: nullishIsSuffix
18193
+ };
18194
+ }
18195
+ /**
18196
+ * Returns true when the node is a string literal or a template literal — the two forms an arktype
18197
+ * definition string is written in.
18198
+ *
18199
+ * @param node - The property value node.
18200
+ * @returns True when the node is a definition string.
18201
+ */ function isDefinitionStringNode(node) {
18202
+ return (node === null || node === void 0 ? void 0 : node.type) === 'Literal' && typeof node.value === 'string' || (node === null || node === void 0 ? void 0 : node.type) === 'TemplateLiteral';
18203
+ }
18204
+ /**
18205
+ * Returns true when the callee is one an arktype object-literal definition is passed to.
18206
+ *
18207
+ * @param callee - The `CallExpression` callee node.
18208
+ * @param definitionCalleeNames - Identifier callee names (e.g. `type`).
18209
+ * @param combinatorMethodNames - Member-expression method names (e.g. `merge`).
18210
+ * @returns True when the call takes an arktype definition.
18211
+ */ function isArktypeDefinitionCallee(callee, definitionCalleeNames, combinatorMethodNames) {
18212
+ var _callee_property;
18213
+ var result = false;
18214
+ if ((callee === null || callee === void 0 ? void 0 : callee.type) === 'Identifier') {
18215
+ result = definitionCalleeNames.includes(callee.name);
18216
+ } else if ((callee === null || callee === void 0 ? void 0 : callee.type) === 'MemberExpression' && ((_callee_property = callee.property) === null || _callee_property === void 0 ? void 0 : _callee_property.type) === 'Identifier') {
18217
+ var _callee_object;
18218
+ // `someType.merge({ … })` / `type.enumerated({ … })`
18219
+ result = combinatorMethodNames.includes(callee.property.name) || ((_callee_object = callee.object) === null || _callee_object === void 0 ? void 0 : _callee_object.type) === 'Identifier' && definitionCalleeNames.includes(callee.object.name);
18220
+ }
18221
+ return result;
18222
+ }
18223
+ /**
18224
+ * Walks out of a property to the outermost object literal it belongs to and returns the arktype call
18225
+ * that literal is an argument of, or null when the property is not part of an arktype definition.
18226
+ *
18227
+ * Gating on the enclosing call keeps the rule off ordinary object literals that happen to hold a
18228
+ * `'… | null | undefined'` string, and off `clearable`'s own implementation.
18229
+ *
18230
+ * @param property - The `Property` node being checked.
18231
+ * @param definitionCalleeNames - Identifier callee names (e.g. `type`).
18232
+ * @param combinatorMethodNames - Member-expression method names (e.g. `merge`).
18233
+ * @returns The enclosing arktype `CallExpression`, or null.
18234
+ */ function arktypeDefinitionCallForProperty(property, definitionCalleeNames, combinatorMethodNames) {
18235
+ var current = property === null || property === void 0 ? void 0 : property.parent;
18236
+ var result = null;
18237
+ while(current != null){
18238
+ var parent = current.parent;
18239
+ if (current.type !== 'ObjectExpression') {
18240
+ current = null;
18241
+ } else if ((parent === null || parent === void 0 ? void 0 : parent.type) === 'Property') {
18242
+ current = parent.parent; // nested definition — climb to the enclosing object literal
18243
+ } else {
18244
+ if ((parent === null || parent === void 0 ? void 0 : parent.type) === 'CallExpression' && isArktypeDefinitionCallee(parent.callee, definitionCalleeNames, combinatorMethodNames)) {
18245
+ result = parent;
18246
+ }
18247
+ current = null;
18248
+ }
18249
+ }
18250
+ return result;
18251
+ }
18252
+ /**
18253
+ * Unwraps a `X.or('null').or('undefined')` / `X.or('null | undefined')` chain.
18254
+ *
18255
+ * @param node - The property value node.
18256
+ * @param sourceCode - The ESLint `SourceCode` object.
18257
+ * @returns The chain's receiver plus the nullish members it appends, or null when the node is not a nullish `.or(...)` chain.
18258
+ */ function unwrapNullishOrChain(node, sourceCode) {
18259
+ var current = node;
18260
+ var receiver = null;
18261
+ var hasNull = false;
18262
+ var hasUndefined = false;
18263
+ while(current != null){
18264
+ var _current_arguments, _callee_property;
18265
+ var callee = current.type === 'CallExpression' ? current.callee : null;
18266
+ var argument = current.type === 'CallExpression' && ((_current_arguments = current.arguments) === null || _current_arguments === void 0 ? void 0 : _current_arguments.length) === 1 ? current.arguments[0] : null;
18267
+ var isOrCall = (callee === null || callee === void 0 ? void 0 : callee.type) === 'MemberExpression' && ((_callee_property = callee.property) === null || _callee_property === void 0 ? void 0 : _callee_property.type) === 'Identifier' && callee.property.name === OR_METHOD_NAME;
18268
+ // only a wholly-nullish argument (`'null'`, `'undefined'`, `'null | undefined'`) is part of the chain
18269
+ var split = isOrCall && argument != null && isDefinitionStringNode(argument) ? splitNullishUnion(sourceCode.getText(argument).slice(1, -1)) : null;
18270
+ if ((split === null || split === void 0 ? void 0 : split.base.trim()) === '') {
18271
+ hasNull = hasNull || split.hasNull;
18272
+ hasUndefined = hasUndefined || split.hasUndefined;
18273
+ receiver = callee.object;
18274
+ current = receiver;
18275
+ } else {
18276
+ current = null;
18277
+ }
18278
+ }
18279
+ return receiver == null ? null : {
18280
+ receiver: receiver,
18281
+ hasNull: hasNull,
18282
+ hasUndefined: hasUndefined
18283
+ };
18284
+ }
18285
+ /**
18286
+ * Returns true when `name` is already bound at the top level of the program — imported, or declared
18287
+ * in the file itself (as it is inside `@dereekb/model`).
18288
+ *
18289
+ * @param programNode - The `Program` node.
18290
+ * @param name - The binding to look for.
18291
+ * @returns True when the name is already available.
18292
+ */ function hasTopLevelBinding(programNode, name) {
18293
+ var _ref;
18294
+ var body = (_ref = programNode === null || programNode === void 0 ? void 0 : programNode.body) !== null && _ref !== void 0 ? _ref : [];
18295
+ var result = false;
18296
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
18297
+ try {
18298
+ for(var _iterator = body[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
18299
+ var statement = _step.value;
18300
+ var declaration = (statement === null || statement === void 0 ? void 0 : statement.type) === 'ExportNamedDeclaration' ? statement.declaration : statement;
18301
+ if ((statement === null || statement === void 0 ? void 0 : statement.type) === 'ImportDeclaration') {
18302
+ var _statement_specifiers;
18303
+ result = result || ((_statement_specifiers = statement.specifiers) !== null && _statement_specifiers !== void 0 ? _statement_specifiers : []).some(function(specifier) {
18304
+ var _specifier_local;
18305
+ return (specifier === null || specifier === void 0 ? void 0 : (_specifier_local = specifier.local) === null || _specifier_local === void 0 ? void 0 : _specifier_local.name) === name;
18306
+ });
18307
+ } else if ((declaration === null || declaration === void 0 ? void 0 : declaration.type) === 'VariableDeclaration') {
18308
+ var _declaration_declarations;
18309
+ result = result || ((_declaration_declarations = declaration.declarations) !== null && _declaration_declarations !== void 0 ? _declaration_declarations : []).some(function(declarator) {
18310
+ var _declarator_id;
18311
+ return (declarator === null || declarator === void 0 ? void 0 : (_declarator_id = declarator.id) === null || _declarator_id === void 0 ? void 0 : _declarator_id.name) === name;
18312
+ });
18313
+ } else if ((declaration === null || declaration === void 0 ? void 0 : declaration.type) === 'FunctionDeclaration') {
18314
+ var _declaration_id;
18315
+ result = result || ((_declaration_id = declaration.id) === null || _declaration_id === void 0 ? void 0 : _declaration_id.name) === name;
18316
+ }
18317
+ }
18318
+ } catch (err) {
18319
+ _didIteratorError = true;
18320
+ _iteratorError = err;
18321
+ } finally{
18322
+ try {
18323
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
18324
+ _iterator.return();
18325
+ }
18326
+ } finally{
18327
+ if (_didIteratorError) {
18328
+ throw _iteratorError;
18329
+ }
18330
+ }
18331
+ }
18332
+ return result;
18333
+ }
18334
+ /**
18335
+ * ESLint rule that requires arktype model/params definitions to express a clearable field with
18336
+ * `clearable('TYPE')` rather than by unioning the nullish keywords inline
18337
+ * (`'TYPE | null | undefined'`) or by appending them with `.or(...)`.
18338
+ *
18339
+ * `clearable(...)` is the workspace's canonical spelling for the `Maybe<T>` fields on a params
18340
+ * interface: it names the semantic (`null` clears the field, `undefined` leaves it unchanged)
18341
+ * instead of restating the union at every property, and it is what the model-api validator's
18342
+ * `MAYBE_WITHOUT_CLEARABLE` check and the JSON Schema export helper both key off. An inline union
18343
+ * decodes the same way today but drifts from both.
18344
+ *
18345
+ * Only properties of an object literal passed to an arktype definition call (`type({ … })`,
18346
+ * `someType.merge({ … })`, …) are considered, so ordinary object literals — and `clearable`'s own
18347
+ * implementation — are left alone.
18348
+ *
18349
+ * The fix rewrites the property value and, when the helper is not already in scope, adds its import
18350
+ * (once per pass; the remaining properties are rewritten in the same pass alongside it). When no
18351
+ * import can be anchored the violation is reported without a fix rather than emitting a reference to
18352
+ * an unimported helper.
18353
+ *
18354
+ * @example
18355
+ * ```ts
18356
+ * // WARN — preferClearableDefinition
18357
+ * export const updateWidgetParamsType = type({
18358
+ * 'name?': 'string | null | undefined',
18359
+ * 'tags?': 'string[] | null | undefined'
18360
+ * });
18361
+ *
18362
+ * // WARN — preferClearableOrChain
18363
+ * export const publishWidgetParamsType = type({
18364
+ * 'entries?': widgetEntryParamsType.array().or('null | undefined')
18365
+ * });
18366
+ *
18367
+ * // OK
18368
+ * export const updateWidgetParamsType = type({
18369
+ * 'name?': clearable('string'),
18370
+ * 'tags?': clearable('string[]')
18371
+ * });
18372
+ * ```
18373
+ */ var FIREBASE_PREFER_CLEARABLE_ARKTYPE_RULE = {
18374
+ meta: {
18375
+ type: 'suggestion',
18376
+ fixable: 'code',
18377
+ docs: {
18378
+ description: 'Require `clearable(...)` from `@dereekb/model` for arktype definitions that union `null` / `undefined`, rather than spelling the nullish union inline.',
18379
+ recommended: true
18380
+ },
18381
+ messages: {
18382
+ preferClearableDefinition: 'Arktype definition `{{definition}}` unions the nullish keywords inline. Use `{{suggestion}}` instead — `{{helper}}(...)` from `{{module}}` is the canonical spelling for a clearable (`Maybe`) field.',
18383
+ preferClearableOrChain: 'Arktype definition `{{definition}}` appends the nullish keywords with `.or(...)`. Use `{{suggestion}}` instead — `{{helper}}(...)` from `{{module}}` is the canonical spelling for a clearable (`Maybe`) field.'
18384
+ },
18385
+ schema: [
18386
+ {
18387
+ type: 'object',
18388
+ additionalProperties: false,
18389
+ properties: {
18390
+ clearableFunctionName: {
18391
+ type: 'string'
18392
+ },
18393
+ importModule: {
18394
+ type: 'string'
18395
+ },
18396
+ autoImport: {
18397
+ type: 'boolean'
18398
+ },
18399
+ includeSingleNullish: {
18400
+ type: 'boolean'
18401
+ },
18402
+ definitionCalleeNames: {
18403
+ type: 'array',
18404
+ items: {
18405
+ type: 'string'
18406
+ }
18407
+ },
18408
+ combinatorMethodNames: {
18409
+ type: 'array',
18410
+ items: {
18411
+ type: 'string'
18412
+ }
18413
+ }
18414
+ }
18415
+ }
18416
+ ]
18417
+ },
18418
+ create: function create(context) {
18419
+ var _context_options_, _options_clearableFunctionName, _options_importModule, _options_definitionCalleeNames, _options_combinatorMethodNames;
18420
+ var options = (_context_options_ = context.options[0]) !== null && _context_options_ !== void 0 ? _context_options_ : {};
18421
+ var clearableName = (_options_clearableFunctionName = options.clearableFunctionName) !== null && _options_clearableFunctionName !== void 0 ? _options_clearableFunctionName : CLEARABLE_FUNCTION_NAME;
18422
+ var importModule = (_options_importModule = options.importModule) !== null && _options_importModule !== void 0 ? _options_importModule : CLEARABLE_IMPORT_MODULE;
18423
+ var autoImport = options.autoImport !== false;
18424
+ var includeSingleNullish = options.includeSingleNullish === true;
18425
+ var definitionCalleeNames = (_options_definitionCalleeNames = options.definitionCalleeNames) !== null && _options_definitionCalleeNames !== void 0 ? _options_definitionCalleeNames : DEFAULT_ARKTYPE_DEFINITION_CALLEE_NAMES;
18426
+ var combinatorMethodNames = (_options_combinatorMethodNames = options.combinatorMethodNames) !== null && _options_combinatorMethodNames !== void 0 ? _options_combinatorMethodNames : DEFAULT_ARKTYPE_COMBINATOR_METHOD_NAMES;
18427
+ var sourceCode = context.sourceCode;
18428
+ var programNode = null;
18429
+ var helperInScope = false;
18430
+ var importAnchored = false;
18431
+ var importFixUsed = false;
18432
+ /**
18433
+ * Returns the import declaration the helper's import is added to — an existing value import from
18434
+ * `importModule` when there is one, otherwise the file's last import (appended after).
18435
+ *
18436
+ * @returns The anchor import and whether the helper merges into its specifier list, or null when the file has no import to anchor to.
18437
+ */ function importAnchorFor() {
18438
+ var _ref;
18439
+ var imports = ((_ref = programNode === null || programNode === void 0 ? void 0 : programNode.body) !== null && _ref !== void 0 ? _ref : []).filter(function(statement) {
18440
+ return (statement === null || statement === void 0 ? void 0 : statement.type) === 'ImportDeclaration';
18441
+ });
18442
+ var mergeable = imports.find(function(statement) {
18443
+ var _statement_specifiers;
18444
+ var _statement_source;
18445
+ return ((_statement_source = statement.source) === null || _statement_source === void 0 ? void 0 : _statement_source.value) === importModule && statement.importKind !== 'type' && ((_statement_specifiers = statement.specifiers) !== null && _statement_specifiers !== void 0 ? _statement_specifiers : []).some(function(specifier) {
18446
+ return (specifier === null || specifier === void 0 ? void 0 : specifier.type) === 'ImportSpecifier';
18447
+ });
18448
+ });
18449
+ var result = null;
18450
+ if (mergeable != null) {
18451
+ result = {
18452
+ node: mergeable,
18453
+ merge: true
18454
+ };
18455
+ } else if (imports.length > 0) {
18456
+ result = {
18457
+ node: imports[imports.length - 1],
18458
+ merge: false
18459
+ };
18460
+ }
18461
+ return result;
18462
+ }
18463
+ /**
18464
+ * Builds the fix that brings the helper into scope, merging into an existing `importModule`
18465
+ * import when there is one.
18466
+ *
18467
+ * @param fixer - The ESLint fixer.
18468
+ * @returns The import fix, or null when no import can be anchored.
18469
+ */ function buildImportFix(fixer) {
18470
+ var anchor = importAnchorFor();
18471
+ var result = null;
18472
+ if ((anchor === null || anchor === void 0 ? void 0 : anchor.merge) === true) {
18473
+ var firstNamed = anchor.node.specifiers.find(function(specifier) {
18474
+ return (specifier === null || specifier === void 0 ? void 0 : specifier.type) === 'ImportSpecifier';
18475
+ });
18476
+ result = fixer.insertTextBefore(firstNamed, "".concat(clearableName, ", "));
18477
+ } else if (anchor != null) {
18478
+ result = fixer.insertTextAfter(anchor.node, "\nimport { ".concat(clearableName, " } from '").concat(importModule, "';"));
18479
+ }
18480
+ return result;
18481
+ }
18482
+ /**
18483
+ * Reports a definition, attaching the rewrite fix — plus the helper's import on the first report
18484
+ * of the pass — only when the result would compile.
18485
+ *
18486
+ * @param valueNode - The definition node to replace.
18487
+ * @param messageId - The message to report.
18488
+ * @param suggestion - The `clearable(...)` replacement text.
18489
+ */ function reportPreferClearable(valueNode, messageId, suggestion) {
18490
+ var data = {
18491
+ definition: sourceCode.getText(valueNode),
18492
+ suggestion: suggestion,
18493
+ helper: clearableName,
18494
+ module: importModule
18495
+ };
18496
+ if (helperInScope || importAnchored) {
18497
+ context.report({
18498
+ node: valueNode,
18499
+ messageId: messageId,
18500
+ data: data,
18501
+ fix: function fix(fixer) {
18502
+ var fixes = [
18503
+ fixer.replaceText(valueNode, suggestion)
18504
+ ];
18505
+ if (!helperInScope && !importFixUsed) {
18506
+ var importFix = buildImportFix(fixer);
18507
+ if (importFix != null) {
18508
+ importFixUsed = true;
18509
+ fixes.push(importFix);
18510
+ }
18511
+ }
18512
+ return fixes;
18513
+ }
18514
+ });
18515
+ } else {
18516
+ // no import can be anchored — reporting a fix here would reference an unimported helper
18517
+ context.report({
18518
+ node: valueNode,
18519
+ messageId: messageId,
18520
+ data: data
18521
+ });
18522
+ }
18523
+ }
18524
+ /**
18525
+ * Checks a string/template definition for an inline nullish union.
18526
+ *
18527
+ * @param valueNode - The definition node.
18528
+ */ function checkDefinitionString(valueNode) {
18529
+ var text = sourceCode.getText(valueNode);
18530
+ var delimiter = text.charAt(0);
18531
+ var split = splitNullishUnion(text.slice(1, -1));
18532
+ var bothNullish = split.hasNull && split.hasUndefined;
18533
+ var anyNullish = split.hasNull || split.hasUndefined;
18534
+ if ((bothNullish || includeSingleNullish && anyNullish) && split.nullishIsSuffix && split.base.trim() !== '') {
18535
+ reportPreferClearable(valueNode, 'preferClearableDefinition', "".concat(clearableName, "(").concat(delimiter).concat(split.base).concat(delimiter, ")"));
18536
+ }
18537
+ }
18538
+ /**
18539
+ * Checks a call expression for a nullish `.or(...)` chain.
18540
+ *
18541
+ * @param valueNode - The definition node.
18542
+ */ function checkOrChain(valueNode) {
18543
+ var chain = unwrapNullishOrChain(valueNode, sourceCode);
18544
+ if (chain != null) {
18545
+ var bothNullish = chain.hasNull && chain.hasUndefined;
18546
+ var anyNullish = chain.hasNull || chain.hasUndefined;
18547
+ if (bothNullish || includeSingleNullish && anyNullish) {
18548
+ reportPreferClearable(valueNode, 'preferClearableOrChain', "".concat(clearableName, "(").concat(sourceCode.getText(chain.receiver), ")"));
18549
+ }
18550
+ }
18551
+ }
18552
+ return {
18553
+ Program: function Program(node) {
18554
+ programNode = node;
18555
+ helperInScope = hasTopLevelBinding(node, clearableName);
18556
+ importFixUsed = false;
18557
+ importAnchored = autoImport && importAnchorFor() != null;
18558
+ },
18559
+ Property: function Property(node) {
18560
+ var valueNode = node === null || node === void 0 ? void 0 : node.value;
18561
+ if (valueNode != null && arktypeDefinitionCallForProperty(node, definitionCalleeNames, combinatorMethodNames) != null) {
18562
+ if (isDefinitionStringNode(valueNode)) {
18563
+ checkDefinitionString(valueNode);
18564
+ } else if (valueNode.type === 'CallExpression') {
18565
+ checkOrChain(valueNode);
18566
+ }
18567
+ }
18568
+ }
18569
+ };
18570
+ }
18571
+ };
18572
+
18079
18573
  /**
18080
18574
  * ESLint plugin for `@dereekb/firebase` rules.
18081
18575
  *
@@ -18099,7 +18593,8 @@ function _unsupported_iterable_to_array(o, minLen) {
18099
18593
  'require-canonical-api-spec-filename': FIREBASE_REQUIRE_CANONICAL_API_SPEC_FILENAME_RULE,
18100
18594
  'require-api-crud-spec-for-group': FIREBASE_REQUIRE_API_CRUD_SPEC_FOR_GROUP_RULE,
18101
18595
  'require-dbx-model-api-params-tag': FIREBASE_REQUIRE_DBX_MODEL_API_PARAMS_TAG_RULE,
18102
- 'require-use-model-roles': FIREBASE_REQUIRE_USE_MODEL_ROLES_RULE
18596
+ 'require-use-model-roles': FIREBASE_REQUIRE_USE_MODEL_ROLES_RULE,
18597
+ 'prefer-clearable-arktype': FIREBASE_PREFER_CLEARABLE_ARKTYPE_RULE
18103
18598
  }
18104
18599
  };
18105
18600
  /**
@@ -18108,4 +18603,4 @@ function _unsupported_iterable_to_array(o, minLen) {
18108
18603
  * @dbxAllowConstantName
18109
18604
  */ var firebaseESLintPlugin = FIREBASE_ESLINT_PLUGIN;
18110
18605
 
18111
- export { API_DETAILS_IMPORT_MODULE, DBX_MODEL_API_PARAMS_MARKER, DBX_MODEL_FIREBASE_INDEX_MARKER, DBX_MODEL_SERVICE_FACTORY_TAG, DEFAULT_API_DETAILS_FACTORY_NAME, DEFAULT_CONSTRAINT_FACTORY_NAMES, DEFAULT_CRUD_FUNCTIONS_CONFIG_SUFFIX, DEFAULT_CRUD_FUNCTION_TYPE_VERBS, DEFAULT_CRUD_VERB_NAMES, DEFAULT_DISCOVERY_EXCLUDED_DIRS, DEFAULT_FACTORY_SEARCH_ROOTS, DEFAULT_FACTORY_TAG, DEFAULT_FIRESTORE_RULES_FILENAME, DEFAULT_FUNCTION_DIR_SEGMENT, DEFAULT_FUNCTION_TYPE_MAP_SUFFIX, DEFAULT_IDENTITY_FACTORY_NAME, DEFAULT_INDEX_AFFECTING_CONSTRAINT_NAMES, DEFAULT_MODEL_MARKER_TAG, DEFAULT_MODEL_SEARCH_ROOTS, DEFAULT_PAGINATION_CONSTRAINT_NAMES, DEFAULT_REGISTRY_FACTORY_CALL_NAME, DEFAULT_STORAGE_FILE_UPLOAD_POLICY_TYPE_NAME, DEFAULT_STORAGE_RULES_FILENAME, DEFAULT_USE_MODEL_METHOD_NAMES, FIREBASE_ESLINT_PLUGIN, FIREBASE_MODEL_SERVICE_FACTORY_MODULE, FIREBASE_MODEL_SERVICE_FACTORY_NAME, FIREBASE_MODULE, FIREBASE_REQUIRE_API_CRUD_SPEC_FOR_GROUP_RULE, FIREBASE_REQUIRE_API_DETAILS_FOR_CRUD_FUNCTION_RULE, FIREBASE_REQUIRE_CANONICAL_API_SPEC_FILENAME_RULE, FIREBASE_REQUIRE_COMPLETE_CRUD_FUNCTION_CONFIG_MAP_RULE, FIREBASE_REQUIRE_DBX_MODEL_API_PARAMS_TAG_RULE, FIREBASE_REQUIRE_DBX_MODEL_COMPANION_TAGS_RULE, FIREBASE_REQUIRE_DBX_MODEL_FIREBASE_INDEX_COMPANION_TAGS_RULE, FIREBASE_REQUIRE_DBX_MODEL_FIREBASE_INDEX_QUERY_SUFFIX_RULE, FIREBASE_REQUIRE_DBX_MODEL_FIREBASE_INDEX_VALID_DISPATCHER_RULE, FIREBASE_REQUIRE_DBX_MODEL_SERVICE_FACTORY_TAG_RULE, FIREBASE_REQUIRE_FIRESTORE_CONSTRAINT_TYPE_PARAMETER_RULE, FIREBASE_REQUIRE_FIRESTORE_RULE_FOR_SERVICE_MODEL_RULE, FIREBASE_REQUIRE_INPUT_TYPE_FOR_API_DETAILS_RULE, FIREBASE_REQUIRE_SERVICE_FACTORY_FOR_DBX_MODEL_RULE, FIREBASE_REQUIRE_STORAGEFILE_POLICY_MATCHES_RULES_RULE, FIREBASE_REQUIRE_TAGGED_FIRESTORE_CONSTRAINTS_RULE, FIREBASE_REQUIRE_USE_MODEL_ROLES_RULE, INPUT_TYPE_PROPERTY_NAME, MIRRORS_POLICY_KEY_MARKER_REGEX, MODEL_FIREBASE_CRUD_FUNCTION_CONFIG_MAP_TYPE_NAME, QUERY_SUFFIX, discoveryGlobExcludeFilter, firebaseESLintPlugin, parseFirestoreRules, parseStorageRules };
18606
+ export { API_DETAILS_IMPORT_MODULE, CLEARABLE_FUNCTION_NAME, CLEARABLE_IMPORT_MODULE, DBX_MODEL_API_PARAMS_MARKER, DBX_MODEL_FIREBASE_INDEX_MARKER, DBX_MODEL_SERVICE_FACTORY_TAG, DEFAULT_API_DETAILS_FACTORY_NAME, DEFAULT_ARKTYPE_COMBINATOR_METHOD_NAMES, DEFAULT_ARKTYPE_DEFINITION_CALLEE_NAMES, DEFAULT_CONSTRAINT_FACTORY_NAMES, DEFAULT_CRUD_FUNCTIONS_CONFIG_SUFFIX, DEFAULT_CRUD_FUNCTION_TYPE_VERBS, DEFAULT_CRUD_VERB_NAMES, DEFAULT_DISCOVERY_EXCLUDED_DIRS, DEFAULT_FACTORY_SEARCH_ROOTS, DEFAULT_FACTORY_TAG, DEFAULT_FIRESTORE_RULES_FILENAME, DEFAULT_FUNCTION_DIR_SEGMENT, DEFAULT_FUNCTION_TYPE_MAP_SUFFIX, DEFAULT_IDENTITY_FACTORY_NAME, DEFAULT_INDEX_AFFECTING_CONSTRAINT_NAMES, DEFAULT_MODEL_MARKER_TAG, DEFAULT_MODEL_SEARCH_ROOTS, DEFAULT_PAGINATION_CONSTRAINT_NAMES, DEFAULT_REGISTRY_FACTORY_CALL_NAME, DEFAULT_STORAGE_FILE_UPLOAD_POLICY_TYPE_NAME, DEFAULT_STORAGE_RULES_FILENAME, DEFAULT_USE_MODEL_METHOD_NAMES, FIREBASE_ESLINT_PLUGIN, FIREBASE_MODEL_SERVICE_FACTORY_MODULE, FIREBASE_MODEL_SERVICE_FACTORY_NAME, FIREBASE_MODULE, FIREBASE_PREFER_CLEARABLE_ARKTYPE_RULE, FIREBASE_REQUIRE_API_CRUD_SPEC_FOR_GROUP_RULE, FIREBASE_REQUIRE_API_DETAILS_FOR_CRUD_FUNCTION_RULE, FIREBASE_REQUIRE_CANONICAL_API_SPEC_FILENAME_RULE, FIREBASE_REQUIRE_COMPLETE_CRUD_FUNCTION_CONFIG_MAP_RULE, FIREBASE_REQUIRE_DBX_MODEL_API_PARAMS_TAG_RULE, FIREBASE_REQUIRE_DBX_MODEL_COMPANION_TAGS_RULE, FIREBASE_REQUIRE_DBX_MODEL_FIREBASE_INDEX_COMPANION_TAGS_RULE, FIREBASE_REQUIRE_DBX_MODEL_FIREBASE_INDEX_QUERY_SUFFIX_RULE, FIREBASE_REQUIRE_DBX_MODEL_FIREBASE_INDEX_VALID_DISPATCHER_RULE, FIREBASE_REQUIRE_DBX_MODEL_SERVICE_FACTORY_TAG_RULE, FIREBASE_REQUIRE_FIRESTORE_CONSTRAINT_TYPE_PARAMETER_RULE, FIREBASE_REQUIRE_FIRESTORE_RULE_FOR_SERVICE_MODEL_RULE, FIREBASE_REQUIRE_INPUT_TYPE_FOR_API_DETAILS_RULE, FIREBASE_REQUIRE_SERVICE_FACTORY_FOR_DBX_MODEL_RULE, FIREBASE_REQUIRE_STORAGEFILE_POLICY_MATCHES_RULES_RULE, FIREBASE_REQUIRE_TAGGED_FIRESTORE_CONSTRAINTS_RULE, FIREBASE_REQUIRE_USE_MODEL_ROLES_RULE, INPUT_TYPE_PROPERTY_NAME, MIRRORS_POLICY_KEY_MARKER_REGEX, MODEL_FIREBASE_CRUD_FUNCTION_CONFIG_MAP_TYPE_NAME, QUERY_SUFFIX, discoveryGlobExcludeFilter, firebaseESLintPlugin, parseFirestoreRules, parseStorageRules };
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@dereekb/firebase/eslint",
3
- "version": "13.36.0",
3
+ "version": "13.38.0",
4
4
  "peerDependencies": {
5
- "@dereekb/util": "13.36.0",
5
+ "@dereekb/util": "13.38.0",
6
6
  "@marcbachmann/cel-js": "^7.6.1",
7
7
  "@typescript-eslint/parser": "8.59.3",
8
8
  "@typescript-eslint/utils": "8.59.3",
9
9
  "typescript": "5.9.3"
10
10
  },
11
11
  "devDependencies": {
12
- "@dereekb/firebase": "13.36.0",
12
+ "@dereekb/firebase": "13.38.0",
13
13
  "eslint": "10.4.0",
14
14
  "firebase": "^12.12.1"
15
15
  },
@@ -15,6 +15,7 @@ export { FIREBASE_REQUIRE_CANONICAL_API_SPEC_FILENAME_RULE, DEFAULT_FUNCTION_DIR
15
15
  export { FIREBASE_REQUIRE_API_CRUD_SPEC_FOR_GROUP_RULE, type FirebaseRequireApiCrudSpecForGroupRuleOptions, type FirebaseRequireApiCrudSpecForGroupRuleDefinition } from './require-api-crud-spec-for-group.rule';
16
16
  export { FIREBASE_REQUIRE_DBX_MODEL_API_PARAMS_TAG_RULE, DBX_MODEL_API_PARAMS_MARKER, DEFAULT_CRUD_FUNCTIONS_CONFIG_SUFFIX, DEFAULT_FUNCTION_TYPE_MAP_SUFFIX, type FirebaseRequireDbxModelApiParamsTagRuleOptions, type FirebaseRequireDbxModelApiParamsTagRuleDefinition } from './require-dbx-model-api-params-tag.rule';
17
17
  export { FIREBASE_REQUIRE_USE_MODEL_ROLES_RULE, DEFAULT_USE_MODEL_METHOD_NAMES, type FirebaseRequireUseModelRolesRuleOptions, type FirebaseRequireUseModelRolesRuleDefinition } from './require-use-model-roles.rule';
18
+ export { FIREBASE_PREFER_CLEARABLE_ARKTYPE_RULE, CLEARABLE_FUNCTION_NAME, CLEARABLE_IMPORT_MODULE, DEFAULT_ARKTYPE_DEFINITION_CALLEE_NAMES, DEFAULT_ARKTYPE_COMBINATOR_METHOD_NAMES, type FirebasePreferClearableArktypeRuleOptions, type FirebasePreferClearableArktypeRuleDefinition } from './prefer-clearable-arktype.rule';
18
19
  export { parseStorageRules, MIRRORS_POLICY_KEY_MARKER_REGEX, type ParsedRuleBranch, type ParsedStorageRulesBlock } from './storage-rules-parser';
19
20
  export { parseFirestoreRules, type ParsedFirestoreMatchBlock } from './firestore-rules-parser';
20
21
  export { FIREBASE_ESLINT_PLUGIN, firebaseESLintPlugin, type FirebaseEslintPlugin } from './plugin';
@@ -15,6 +15,7 @@ import { FIREBASE_REQUIRE_CANONICAL_API_SPEC_FILENAME_RULE } from './require-can
15
15
  import { FIREBASE_REQUIRE_API_CRUD_SPEC_FOR_GROUP_RULE } from './require-api-crud-spec-for-group.rule';
16
16
  import { FIREBASE_REQUIRE_DBX_MODEL_API_PARAMS_TAG_RULE } from './require-dbx-model-api-params-tag.rule';
17
17
  import { FIREBASE_REQUIRE_USE_MODEL_ROLES_RULE } from './require-use-model-roles.rule';
18
+ import { FIREBASE_PREFER_CLEARABLE_ARKTYPE_RULE } from './prefer-clearable-arktype.rule';
18
19
  /**
19
20
  * ESLint plugin interface for `@dereekb/firebase` rules.
20
21
  */
@@ -37,6 +38,7 @@ export interface FirebaseEslintPlugin {
37
38
  readonly 'require-api-crud-spec-for-group': typeof FIREBASE_REQUIRE_API_CRUD_SPEC_FOR_GROUP_RULE;
38
39
  readonly 'require-dbx-model-api-params-tag': typeof FIREBASE_REQUIRE_DBX_MODEL_API_PARAMS_TAG_RULE;
39
40
  readonly 'require-use-model-roles': typeof FIREBASE_REQUIRE_USE_MODEL_ROLES_RULE;
41
+ readonly 'prefer-clearable-arktype': typeof FIREBASE_PREFER_CLEARABLE_ARKTYPE_RULE;
40
42
  };
41
43
  }
42
44
  /**