@dereekb/dbx-components-mcp 14.14.0 → 14.16.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.
package/dbx-components-mcp.js
CHANGED
|
@@ -10,7 +10,7 @@ var __export = (target, all) => {
|
|
|
10
10
|
// packages/dbx-components-mcp/package.json
|
|
11
11
|
var package_default = {
|
|
12
12
|
name: "@dereekb/dbx-components-mcp",
|
|
13
|
-
version: "14.
|
|
13
|
+
version: "14.16.0",
|
|
14
14
|
sideEffects: false,
|
|
15
15
|
type: "module",
|
|
16
16
|
bin: {
|
|
@@ -22,8 +22,8 @@ var package_default = {
|
|
|
22
22
|
"ts-morph": "^21.0.0"
|
|
23
23
|
},
|
|
24
24
|
peerDependencies: {
|
|
25
|
-
"@dereekb/dbx-cli": "14.
|
|
26
|
-
"@dereekb/util": "14.
|
|
25
|
+
"@dereekb/dbx-cli": "14.16.0",
|
|
26
|
+
"@dereekb/util": "14.16.0"
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
29
|
|
|
@@ -2496,10 +2496,18 @@ var FIREBASE_MODELS = [
|
|
|
2496
2496
|
tsType: "Maybe<NotificationHealthCheck>",
|
|
2497
2497
|
optional: true,
|
|
2498
2498
|
description: "The result of the most recent notification delivery health check run for this user."
|
|
2499
|
+
},
|
|
2500
|
+
{
|
|
2501
|
+
name: "tso",
|
|
2502
|
+
longName: "textStoppedPhoneNumbers",
|
|
2503
|
+
converter: "optionalFirestoreArray<E164PhoneNumber>({ filterUnique: true, dontStoreIfEmpty: true })",
|
|
2504
|
+
tsType: "Maybe<E164PhoneNumber[]>",
|
|
2505
|
+
optional: true,
|
|
2506
|
+
description: "Phone numbers that replied STOP to a text. Texts to a number in this list resolve to off, at send time and in the health check."
|
|
2499
2507
|
}
|
|
2500
2508
|
],
|
|
2501
2509
|
enums: [],
|
|
2502
|
-
detectionHints: ["b", "x", "dc", "gc", "bc", "ns", "hc"],
|
|
2510
|
+
detectionHints: ["b", "x", "dc", "gc", "bc", "ns", "hc", "tso"],
|
|
2503
2511
|
description: "A global notification user profile that tracks notification preferences and box subscriptions.",
|
|
2504
2512
|
modelGroup: "Notification",
|
|
2505
2513
|
collectionKind: "root",
|
|
@@ -6492,15 +6500,16 @@ function parseSignalInput(property) {
|
|
|
6492
6500
|
...defaultValue === void 0 ? {} : { defaultValue }
|
|
6493
6501
|
};
|
|
6494
6502
|
}
|
|
6503
|
+
var SIGNAL_INPUT_FUNCTION_NAMES = /* @__PURE__ */ new Set(["input", "model"]);
|
|
6495
6504
|
function classifySignalInputCall(call) {
|
|
6496
6505
|
const expression = call.getExpression();
|
|
6497
6506
|
let result;
|
|
6498
|
-
if (Node9.isIdentifier(expression) && expression.getText()
|
|
6507
|
+
if (Node9.isIdentifier(expression) && SIGNAL_INPUT_FUNCTION_NAMES.has(expression.getText())) {
|
|
6499
6508
|
result = "plain";
|
|
6500
6509
|
} else if (Node9.isPropertyAccessExpression(expression)) {
|
|
6501
6510
|
const baseExpr = expression.getExpression();
|
|
6502
6511
|
const propertyName = expression.getName();
|
|
6503
|
-
if (Node9.isIdentifier(baseExpr) && baseExpr.getText()
|
|
6512
|
+
if (Node9.isIdentifier(baseExpr) && SIGNAL_INPUT_FUNCTION_NAMES.has(baseExpr.getText()) && propertyName === "required") {
|
|
6504
6513
|
result = "required";
|
|
6505
6514
|
}
|
|
6506
6515
|
}
|
|
@@ -24747,9 +24756,9 @@ var RULE_CATALOG = [
|
|
|
24747
24756
|
{
|
|
24748
24757
|
code: "NOTIF_TASK_IN_VALIDATE_WITHOUT_HANDLER",
|
|
24749
24758
|
source: "dbx_notification_m_validate_app",
|
|
24750
|
-
severity: "
|
|
24759
|
+
severity: "error",
|
|
24751
24760
|
title: "A task type appears in `validate:` but has no matching `handlers:` registration",
|
|
24752
|
-
whatItFlags: "A task type appears in `validate:` but has no matching `handlers:` registration.",
|
|
24761
|
+
whatItFlags: "A task type appears in `validate:` but has no matching `handlers:` registration.\n`notificationTaskService()` throws at startup for this.",
|
|
24753
24762
|
whenItApplies: "When `notificationTaskService({ validate, handlers })` lists a task type in `validate:` that the `handlers:` traversal doesn't reach.",
|
|
24754
24763
|
whenItDoesNotApply: "Tasks whose handler is provided by an upstream module (covered by the trust list).",
|
|
24755
24764
|
canonicalFix: "Add the matching `NotificationTaskServiceTaskHandlerConfig` for the task and include it in `handlers:`."
|
|
@@ -24870,6 +24879,54 @@ var RULE_CATALOG = [
|
|
|
24870
24879
|
whenItDoesNotApply: "Spreads from trust-listed `@dereekb/*` modules.",
|
|
24871
24880
|
canonicalFix: "Declare the missing function or import it from a trusted module."
|
|
24872
24881
|
},
|
|
24882
|
+
{
|
|
24883
|
+
code: "NOTIF_TEMPLATE_FACTORY_UNLISTED_DELIVERY_METHOD",
|
|
24884
|
+
source: "dbx_notification_m_validate_app",
|
|
24885
|
+
severity: "warning",
|
|
24886
|
+
title: "A template's message factory returns channel content for a delivery method its `NotificationTemplateTypeInfo` does not list in `userConfigurableDeliveryMethods` or `forcedDeliveryMethods`",
|
|
24887
|
+
whatItFlags: "A template's message factory returns channel content for a delivery method its `NotificationTemplateTypeInfo` does not list in `userConfigurableDeliveryMethods` or `forcedDeliveryMethods`.",
|
|
24888
|
+
whenItApplies: "Template types whose info declares an explicit `userConfigurableDeliveryMethods` array, or only a `forcedDeliveryMethods` array (forcing a method changes the `userConfigurableDeliveryMethods` default to none), and whose handler factory (or the function its `factory:` names) builds an object literal with `emailContent`, `textContent` or `notificationSummaryContent` for a method neither array lists.",
|
|
24889
|
+
whenItDoesNotApply: "Infos with neither `userConfigurableDeliveryMethods` nor `forcedDeliveryMethods` (the defaults list email, text and notification summary), lists the scanner cannot read statically (including a `forcedDeliveryMethods` it cannot read), and content built outside the factory's own source \u2014 the send pipeline's runtime warning (`notificationMessageFunctionWithUnlistedDeliveryMethodsWarning`) still covers those.",
|
|
24890
|
+
canonicalFix: "Stop returning the unlisted content from the factory, or add the delivery method to the info's `userConfigurableDeliveryMethods` (or `forcedDeliveryMethods`).",
|
|
24891
|
+
seeAlso: [
|
|
24892
|
+
{
|
|
24893
|
+
kind: "artifact",
|
|
24894
|
+
target: "notification-template"
|
|
24895
|
+
}
|
|
24896
|
+
]
|
|
24897
|
+
},
|
|
24898
|
+
{
|
|
24899
|
+
code: "NOTIF_TEMPLATE_FORCED_DELIVERY_METHOD_EXPLICIT_OPT_IN",
|
|
24900
|
+
source: "dbx_notification_m_validate_app",
|
|
24901
|
+
severity: "error",
|
|
24902
|
+
title: "A `NotificationTemplateTypeInfo` forces a delivery method while `onlySendToExplicitlyEnabledRecipients` is `true`. A forced method is on by default, which an opt-in only type cannot be, and the app throws at startup when the info record is built",
|
|
24903
|
+
whatItFlags: "A `NotificationTemplateTypeInfo` forces a delivery method while `onlySendToExplicitlyEnabledRecipients` is `true`. A forced method is on by default, which an opt-in only type cannot be, and the app throws at startup when the info record is built.",
|
|
24904
|
+
whenItApplies: "Infos with a non-empty statically read `forcedDeliveryMethods` array and `onlySendToExplicitlyEnabledRecipients: true`.",
|
|
24905
|
+
whenItDoesNotApply: "Infos without `forcedDeliveryMethods`, lists the scanner cannot read statically, and `onlySendToExplicitlyEnabledRecipients` values other than a `true` literal.",
|
|
24906
|
+
canonicalFix: "Remove `forcedDeliveryMethods`, or remove `onlySendToExplicitlyEnabledRecipients` from the info.",
|
|
24907
|
+
seeAlso: [
|
|
24908
|
+
{
|
|
24909
|
+
kind: "artifact",
|
|
24910
|
+
target: "notification-template"
|
|
24911
|
+
}
|
|
24912
|
+
]
|
|
24913
|
+
},
|
|
24914
|
+
{
|
|
24915
|
+
code: "NOTIF_TEMPLATE_FORCED_TEXT_DELIVERY_METHOD",
|
|
24916
|
+
source: "dbx_notification_m_validate_app",
|
|
24917
|
+
severity: "error",
|
|
24918
|
+
title: "A `NotificationTemplateTypeInfo` lists the text delivery method in `forcedDeliveryMethods`. Texts cannot be forced, and the app throws at startup when the info record is built",
|
|
24919
|
+
whatItFlags: "A `NotificationTemplateTypeInfo` lists the text delivery method in `forcedDeliveryMethods`. Texts cannot be forced, and the app throws at startup when the info record is built.",
|
|
24920
|
+
whenItApplies: "Infos whose statically read `forcedDeliveryMethods` array contains `NotificationDeliveryMethod.TEXT`.",
|
|
24921
|
+
whenItDoesNotApply: "Infos without `forcedDeliveryMethods`, and lists the scanner cannot read statically.",
|
|
24922
|
+
canonicalFix: "Remove `NotificationDeliveryMethod.TEXT` from `forcedDeliveryMethods`. Texts stay opt-in for the recipient.",
|
|
24923
|
+
seeAlso: [
|
|
24924
|
+
{
|
|
24925
|
+
kind: "artifact",
|
|
24926
|
+
target: "notification-template"
|
|
24927
|
+
}
|
|
24928
|
+
]
|
|
24929
|
+
},
|
|
24873
24930
|
{
|
|
24874
24931
|
code: "NOTIF_TEMPLATE_INFO_MISSING",
|
|
24875
24932
|
source: "dbx_notification_m_validate_app",
|
|
@@ -30840,6 +30897,16 @@ function readStringProperty2(obj, name) {
|
|
|
30840
30897
|
}
|
|
30841
30898
|
return void 0;
|
|
30842
30899
|
}
|
|
30900
|
+
function readBooleanProperty2(obj, name) {
|
|
30901
|
+
const init = unwrapAsExpressions(getPropertyInitializer(obj, name));
|
|
30902
|
+
let result;
|
|
30903
|
+
if (init?.getKind() === SyntaxKind11.TrueKeyword) {
|
|
30904
|
+
result = true;
|
|
30905
|
+
} else if (init?.getKind() === SyntaxKind11.FalseKeyword) {
|
|
30906
|
+
result = false;
|
|
30907
|
+
}
|
|
30908
|
+
return result;
|
|
30909
|
+
}
|
|
30843
30910
|
function readIdentifierProperty(obj, name) {
|
|
30844
30911
|
const init = unwrapAsExpressions(getPropertyInitializer(obj, name));
|
|
30845
30912
|
if (init && Node26.isIdentifier(init)) {
|
|
@@ -31566,7 +31633,8 @@ __export(notification_m_validate_app_exports, {
|
|
|
31566
31633
|
extractAppNotifications: () => extractAppNotifications,
|
|
31567
31634
|
formatResult: () => formatResult6,
|
|
31568
31635
|
inspectAppNotifications: () => inspectAppNotifications,
|
|
31569
|
-
validateAppNotifications: () => validateAppNotifications
|
|
31636
|
+
validateAppNotifications: () => validateAppNotifications,
|
|
31637
|
+
validateExtractedAppNotifications: () => validateExtractedAppNotifications
|
|
31570
31638
|
});
|
|
31571
31639
|
|
|
31572
31640
|
// packages/dbx-cli/validate/src/lib/notification-m-validate-app/extract.ts
|
|
@@ -31587,6 +31655,27 @@ var TEMPLATE_CONFIGS_ARRAY_FACTORY_SUFFIX = "NotificationTemplateServiceConfigsA
|
|
|
31587
31655
|
var TEMPLATE_CONFIGS_ARRAY_TOKEN = "NOTIFICATION_TEMPLATE_SERVICE_CONFIGS_ARRAY_TOKEN";
|
|
31588
31656
|
var CHECKPOINT_ALIAS_SUFFIX = "NotificationTaskCheckpoint";
|
|
31589
31657
|
var TASK_DATA_INTERFACE_SUFFIX = "NotificationTaskData";
|
|
31658
|
+
var USER_CONFIGURABLE_DELIVERY_METHODS_PROPERTY = "userConfigurableDeliveryMethods";
|
|
31659
|
+
var FORCED_DELIVERY_METHODS_PROPERTY = "forcedDeliveryMethods";
|
|
31660
|
+
var ONLY_SEND_TO_EXPLICITLY_ENABLED_RECIPIENTS_PROPERTY = "onlySendToExplicitlyEnabledRecipients";
|
|
31661
|
+
var DELIVERY_METHOD_ENUM = "NotificationDeliveryMethod";
|
|
31662
|
+
var DELIVERY_METHOD_BY_MEMBER = /* @__PURE__ */ new Map([
|
|
31663
|
+
["EMAIL", "EMAIL"],
|
|
31664
|
+
["TEXT", "TEXT"],
|
|
31665
|
+
["PUSH", "PUSH"],
|
|
31666
|
+
["NOTIFICATION_SUMMARY", "NOTIFICATION_SUMMARY"]
|
|
31667
|
+
]);
|
|
31668
|
+
var DELIVERY_METHOD_BY_CODE = /* @__PURE__ */ new Map([
|
|
31669
|
+
["e", "EMAIL"],
|
|
31670
|
+
["t", "TEXT"],
|
|
31671
|
+
["p", "PUSH"],
|
|
31672
|
+
["n", "NOTIFICATION_SUMMARY"]
|
|
31673
|
+
]);
|
|
31674
|
+
var CONTENT_PROPERTY_DELIVERY_METHODS = /* @__PURE__ */ new Map([
|
|
31675
|
+
["emailContent", "EMAIL"],
|
|
31676
|
+
["textContent", "TEXT"],
|
|
31677
|
+
["notificationSummaryContent", "NOTIFICATION_SUMMARY"]
|
|
31678
|
+
]);
|
|
31590
31679
|
function extractAppNotifications(inspection) {
|
|
31591
31680
|
const { componentSources, apiSources } = buildInMemoryProject(inspection);
|
|
31592
31681
|
const trustedExternalIdentifiers = collectTrustedExternalIdentifiers([...componentSources, ...apiSources]);
|
|
@@ -31660,13 +31749,20 @@ function extractTemplateTypeInfos(sources) {
|
|
|
31660
31749
|
if (!nameMatches && !typeMatches) continue;
|
|
31661
31750
|
const obj = asObjectLiteral(decl.getInitializer());
|
|
31662
31751
|
if (!obj) continue;
|
|
31752
|
+
const deliveryMethods = readDeliveryMethodsProperty(obj, sf, USER_CONFIGURABLE_DELIVERY_METHODS_PROPERTY);
|
|
31753
|
+
const forcedDeliveryMethods = readDeliveryMethodsProperty(obj, sf, FORCED_DELIVERY_METHODS_PROPERTY);
|
|
31663
31754
|
const entry = {
|
|
31664
31755
|
symbolName: name,
|
|
31665
31756
|
typeConstantName: readIdentifierProperty(obj, "type"),
|
|
31666
31757
|
humanName: readStringProperty2(obj, "name"),
|
|
31667
31758
|
description: readStringProperty2(obj, "description"),
|
|
31668
|
-
|
|
31759
|
+
notificationModelIdentity: readIdentifierProperty(obj, "notificationModelIdentity"),
|
|
31669
31760
|
targetModelIdentity: readIdentifierProperty(obj, "targetModelIdentity"),
|
|
31761
|
+
userConfigurableDeliveryMethods: deliveryMethods.methods,
|
|
31762
|
+
userConfigurableDeliveryMethodsSource: deliveryMethods.source,
|
|
31763
|
+
forcedDeliveryMethods: forcedDeliveryMethods.methods,
|
|
31764
|
+
forcedDeliveryMethodsSource: forcedDeliveryMethods.source,
|
|
31765
|
+
onlySendToExplicitlyEnabledRecipients: readBooleanProperty2(obj, ONLY_SEND_TO_EXPLICITLY_ENABLED_RECIPIENTS_PROPERTY),
|
|
31670
31766
|
sourceFile: rel,
|
|
31671
31767
|
line: decl.getStartLineNumber()
|
|
31672
31768
|
};
|
|
@@ -31676,6 +31772,44 @@ function extractTemplateTypeInfos(sources) {
|
|
|
31676
31772
|
}
|
|
31677
31773
|
return out;
|
|
31678
31774
|
}
|
|
31775
|
+
function readDeliveryMethodsProperty(obj, sf, propertyName) {
|
|
31776
|
+
const value = unwrapAsExpressions(getPropertyInitializer(obj, propertyName));
|
|
31777
|
+
let result;
|
|
31778
|
+
if (!value || isNullishLiteral(value)) {
|
|
31779
|
+
result = { methods: void 0, source: "default" };
|
|
31780
|
+
} else if (Node28.isIdentifier(value)) {
|
|
31781
|
+
result = readDeliveryMethodsArray(asArrayLiteral(findLocalVariable(sf, value.getText())?.getInitializer()));
|
|
31782
|
+
} else {
|
|
31783
|
+
result = readDeliveryMethodsArray(asArrayLiteral(value));
|
|
31784
|
+
}
|
|
31785
|
+
return result;
|
|
31786
|
+
}
|
|
31787
|
+
function readDeliveryMethodsArray(arr) {
|
|
31788
|
+
const methods = [];
|
|
31789
|
+
let readable = arr !== void 0;
|
|
31790
|
+
for (const el of arr?.getElements() ?? []) {
|
|
31791
|
+
const method = readDeliveryMethodElement(unwrapAsExpressions(el));
|
|
31792
|
+
if (!method) {
|
|
31793
|
+
readable = false;
|
|
31794
|
+
} else if (!methods.includes(method)) {
|
|
31795
|
+
methods.push(method);
|
|
31796
|
+
}
|
|
31797
|
+
}
|
|
31798
|
+
const result = readable ? { methods, source: "declared" } : { methods: void 0, source: "unresolved" };
|
|
31799
|
+
return result;
|
|
31800
|
+
}
|
|
31801
|
+
function readDeliveryMethodElement(el) {
|
|
31802
|
+
let result;
|
|
31803
|
+
if (el && Node28.isPropertyAccessExpression(el) && el.getExpression().getText() === DELIVERY_METHOD_ENUM) {
|
|
31804
|
+
result = DELIVERY_METHOD_BY_MEMBER.get(el.getName());
|
|
31805
|
+
} else if (el && Node28.isStringLiteral(el)) {
|
|
31806
|
+
result = DELIVERY_METHOD_BY_CODE.get(el.getLiteralText());
|
|
31807
|
+
}
|
|
31808
|
+
return result;
|
|
31809
|
+
}
|
|
31810
|
+
function isNullishLiteral(node) {
|
|
31811
|
+
return node !== void 0 && (Node28.isIdentifier(node) && node.getText() === "undefined" || node.getKind() === SyntaxKind12.NullKeyword);
|
|
31812
|
+
}
|
|
31679
31813
|
function extractTemplateInfoAggregates(sources) {
|
|
31680
31814
|
const out = [];
|
|
31681
31815
|
for (const sf of sources) {
|
|
@@ -32037,6 +32171,7 @@ function collectFromSingleEntryFactory(name, out, index) {
|
|
|
32037
32171
|
out.push({
|
|
32038
32172
|
typeIdentifier: typeIdent,
|
|
32039
32173
|
factoryFunctionName: name,
|
|
32174
|
+
contentDeliveryMethods: collectContentDeliveryMethods([fn.node, factoryPropertyFunctionNode(obj, index)]),
|
|
32040
32175
|
sourceFile: fn.relPath,
|
|
32041
32176
|
line: obj.getStartLineNumber()
|
|
32042
32177
|
});
|
|
@@ -32063,7 +32198,7 @@ function consumeArrayEntryFactoryElement(options) {
|
|
|
32063
32198
|
chaseDirectSingleEntryFactoryCall(inner, out, index);
|
|
32064
32199
|
return;
|
|
32065
32200
|
}
|
|
32066
|
-
recordInlineEntryFactoryObject({ inner, factoryName, relPath, out });
|
|
32201
|
+
recordInlineEntryFactoryObject({ inner, factoryName, relPath, out, index });
|
|
32067
32202
|
}
|
|
32068
32203
|
function chaseSpreadEntryFactoryCall(inner, out, index) {
|
|
32069
32204
|
if (!Node28.isCallExpression(inner)) return;
|
|
@@ -32080,12 +32215,40 @@ function chaseDirectSingleEntryFactoryCall(inner, out, index) {
|
|
|
32080
32215
|
}
|
|
32081
32216
|
}
|
|
32082
32217
|
function recordInlineEntryFactoryObject(options) {
|
|
32083
|
-
const { inner, factoryName, relPath, out } = options;
|
|
32218
|
+
const { inner, factoryName, relPath, out, index } = options;
|
|
32084
32219
|
const obj = asObjectLiteral(inner);
|
|
32085
32220
|
if (!obj) return;
|
|
32086
32221
|
const typeIdent = readIdentifierProperty(obj, "type");
|
|
32087
32222
|
if (!typeIdent) return;
|
|
32088
|
-
|
|
32223
|
+
const contentDeliveryMethods = collectContentDeliveryMethods([obj, factoryPropertyFunctionNode(obj, index)]);
|
|
32224
|
+
out.push({ typeIdentifier: typeIdent, factoryFunctionName: factoryName, contentDeliveryMethods, sourceFile: relPath, line: obj.getStartLineNumber() });
|
|
32225
|
+
}
|
|
32226
|
+
function factoryPropertyFunctionNode(obj, index) {
|
|
32227
|
+
const name = readIdentifierProperty(obj, "factory");
|
|
32228
|
+
return name ? index.functionsByName.get(name)?.node : void 0;
|
|
32229
|
+
}
|
|
32230
|
+
function collectContentDeliveryMethods(nodes) {
|
|
32231
|
+
const found = /* @__PURE__ */ new Set();
|
|
32232
|
+
for (const node of nodes) {
|
|
32233
|
+
if (node) {
|
|
32234
|
+
scanContentDeliveryMethods(node, found);
|
|
32235
|
+
}
|
|
32236
|
+
}
|
|
32237
|
+
return Array.from(CONTENT_PROPERTY_DELIVERY_METHODS.values()).filter((method) => found.has(method));
|
|
32238
|
+
}
|
|
32239
|
+
function scanContentDeliveryMethods(node, found) {
|
|
32240
|
+
for (const prop of node.getDescendantsOfKind(SyntaxKind12.PropertyAssignment)) {
|
|
32241
|
+
const method = CONTENT_PROPERTY_DELIVERY_METHODS.get(prop.getName());
|
|
32242
|
+
if (method && !isNullishLiteral(unwrapAsExpressions(prop.getInitializer()))) {
|
|
32243
|
+
found.add(method);
|
|
32244
|
+
}
|
|
32245
|
+
}
|
|
32246
|
+
for (const prop of node.getDescendantsOfKind(SyntaxKind12.ShorthandPropertyAssignment)) {
|
|
32247
|
+
const method = CONTENT_PROPERTY_DELIVERY_METHODS.get(prop.getName());
|
|
32248
|
+
if (method) {
|
|
32249
|
+
found.add(method);
|
|
32250
|
+
}
|
|
32251
|
+
}
|
|
32089
32252
|
}
|
|
32090
32253
|
function extractTaskServiceCalls(sources, index, trustedExternal) {
|
|
32091
32254
|
const out = [];
|
|
@@ -32373,6 +32536,8 @@ function runRules6(inspection, extracted) {
|
|
|
32373
32536
|
checkTemplateInfoPairing(extracted, violations);
|
|
32374
32537
|
checkTemplateInfoRecord(extracted, violations);
|
|
32375
32538
|
checkTemplateHandlerFactory(extracted, violations);
|
|
32539
|
+
checkTemplateDeliveryMethods(extracted, violations);
|
|
32540
|
+
checkTemplateForcedDeliveryMethods(extracted, violations);
|
|
32376
32541
|
checkTasks(extracted, violations);
|
|
32377
32542
|
checkDuplicates2(extracted, violations);
|
|
32378
32543
|
return violations;
|
|
@@ -32541,6 +32706,63 @@ function checkTemplateHandlerFactory(extracted, violations) {
|
|
|
32541
32706
|
});
|
|
32542
32707
|
}
|
|
32543
32708
|
}
|
|
32709
|
+
function checkTemplateDeliveryMethods(extracted, violations) {
|
|
32710
|
+
const infoByTypeConstant = /* @__PURE__ */ new Map();
|
|
32711
|
+
for (const info of extracted.templateTypeInfos) {
|
|
32712
|
+
if (info.typeConstantName) infoByTypeConstant.set(info.typeConstantName, info);
|
|
32713
|
+
}
|
|
32714
|
+
for (const entry of extracted.templateHandlerEntries) {
|
|
32715
|
+
const info = infoByTypeConstant.get(entry.typeIdentifier);
|
|
32716
|
+
const userConfigurable = info ? staticUserConfigurableDeliveryMethods(info) : void 0;
|
|
32717
|
+
if (!info || !userConfigurable) continue;
|
|
32718
|
+
const forced = info.forcedDeliveryMethods ?? [];
|
|
32719
|
+
const listed = [...userConfigurable, ...forced.filter((method) => !userConfigurable.includes(method))];
|
|
32720
|
+
const unlisted = entry.contentDeliveryMethods.filter((method) => !listed.includes(method));
|
|
32721
|
+
if (unlisted.length === 0) continue;
|
|
32722
|
+
const listedText = listed.length > 0 ? listed.join(", ") : "none";
|
|
32723
|
+
const forcedSuffix = forced.length > 0 ? " and `forcedDeliveryMethods`" : "";
|
|
32724
|
+
const listedIn = info.userConfigurableDeliveryMethodsSource === "declared" ? `\`userConfigurableDeliveryMethods\`${forcedSuffix}` : "`forcedDeliveryMethods` (it leaves `userConfigurableDeliveryMethods` out, so it is only sent by its forced methods)";
|
|
32725
|
+
pushViolation8(violations, {
|
|
32726
|
+
code: "NOTIF_TEMPLATE_FACTORY_UNLISTED_DELIVERY_METHOD",
|
|
32727
|
+
severity: "warning",
|
|
32728
|
+
message: `Template handler factory \`${entry.factoryFunctionName ?? "<anonymous>"}\` for \`${entry.typeIdentifier}\` builds content for ${unlisted.join(", ")}, but \`${info.symbolName}\` only lists ${listedText} in ${listedIn}. This is the build-time counterpart of the send pipeline's runtime unlisted-delivery-method warning.`,
|
|
32729
|
+
side: "api",
|
|
32730
|
+
file: entry.sourceFile
|
|
32731
|
+
});
|
|
32732
|
+
}
|
|
32733
|
+
}
|
|
32734
|
+
function staticUserConfigurableDeliveryMethods(info) {
|
|
32735
|
+
let result;
|
|
32736
|
+
if (info.forcedDeliveryMethodsSource !== "unresolved") {
|
|
32737
|
+
if (info.userConfigurableDeliveryMethodsSource === "declared") {
|
|
32738
|
+
result = info.userConfigurableDeliveryMethods;
|
|
32739
|
+
} else if (info.userConfigurableDeliveryMethodsSource === "default" && (info.forcedDeliveryMethods ?? []).some((method) => method !== "TEXT")) {
|
|
32740
|
+
result = [];
|
|
32741
|
+
}
|
|
32742
|
+
}
|
|
32743
|
+
return result;
|
|
32744
|
+
}
|
|
32745
|
+
function checkTemplateForcedDeliveryMethods(extracted, violations) {
|
|
32746
|
+
for (const info of extracted.templateTypeInfos) {
|
|
32747
|
+
const forced = info.forcedDeliveryMethodsSource === "declared" ? info.forcedDeliveryMethods ?? [] : [];
|
|
32748
|
+
if (forced.includes("TEXT")) {
|
|
32749
|
+
pushViolation8(violations, {
|
|
32750
|
+
code: "NOTIF_TEMPLATE_FORCED_TEXT_DELIVERY_METHOD",
|
|
32751
|
+
message: `Info \`${info.symbolName}\` lists TEXT in \`forcedDeliveryMethods\`. Texts cannot be forced; the info record throws at startup.`,
|
|
32752
|
+
side: "component",
|
|
32753
|
+
file: info.sourceFile
|
|
32754
|
+
});
|
|
32755
|
+
}
|
|
32756
|
+
if (forced.length > 0 && info.onlySendToExplicitlyEnabledRecipients === true) {
|
|
32757
|
+
pushViolation8(violations, {
|
|
32758
|
+
code: "NOTIF_TEMPLATE_FORCED_DELIVERY_METHOD_EXPLICIT_OPT_IN",
|
|
32759
|
+
message: `Info \`${info.symbolName}\` forces ${forced.join(", ")} while \`onlySendToExplicitlyEnabledRecipients\` is true. A forced method is on by default, so the info record throws at startup.`,
|
|
32760
|
+
side: "component",
|
|
32761
|
+
file: info.sourceFile
|
|
32762
|
+
});
|
|
32763
|
+
}
|
|
32764
|
+
}
|
|
32765
|
+
}
|
|
32544
32766
|
function checkTasks(extracted, violations) {
|
|
32545
32767
|
if (extracted.taskTypeConstants.length === 0) return;
|
|
32546
32768
|
flagTasksMissingFromAllArrays(extracted, violations);
|
|
@@ -32687,8 +32909,7 @@ function flagValidateWithoutHandler(options) {
|
|
|
32687
32909
|
if (!lenientHandlerTypeIdentifiers.has(name) && !extracted.trustedExternalIdentifiers.has(name)) {
|
|
32688
32910
|
pushViolation8(violations, {
|
|
32689
32911
|
code: "NOTIF_TASK_IN_VALIDATE_WITHOUT_HANDLER",
|
|
32690
|
-
|
|
32691
|
-
message: `Task type \`${name}\` appears in \`notificationTaskService({ validate })\` but has no matching handler in the \`handlers\` array.`,
|
|
32912
|
+
message: `Task type \`${name}\` appears in \`notificationTaskService({ validate })\` but has no matching handler in the \`handlers\` array. \`notificationTaskService()\` throws at startup for this.`,
|
|
32692
32913
|
side: "api",
|
|
32693
32914
|
file: call.sourceFile
|
|
32694
32915
|
});
|
|
@@ -32874,12 +33095,11 @@ async function inspectAppNotifications(componentDir, apiDir) {
|
|
|
32874
33095
|
}
|
|
32875
33096
|
|
|
32876
33097
|
// packages/dbx-cli/validate/src/lib/notification-m-validate-app/index.ts
|
|
32877
|
-
function
|
|
32878
|
-
const extracted = extractAppNotifications(inspection);
|
|
33098
|
+
function validateExtractedAppNotifications(input) {
|
|
32879
33099
|
const violations = [];
|
|
32880
33100
|
let errorCount = 0;
|
|
32881
33101
|
let warningCount = 0;
|
|
32882
|
-
for (const v of runRules6(inspection, extracted)) {
|
|
33102
|
+
for (const v of runRules6(input.inspection, input.extracted)) {
|
|
32883
33103
|
violations.push(v);
|
|
32884
33104
|
if (v.severity === "error") {
|
|
32885
33105
|
errorCount += 1;
|
|
@@ -32891,11 +33111,14 @@ function validateAppNotifications(inspection, options) {
|
|
|
32891
33111
|
violations,
|
|
32892
33112
|
errorCount,
|
|
32893
33113
|
warningCount,
|
|
32894
|
-
componentDir:
|
|
32895
|
-
apiDir:
|
|
33114
|
+
componentDir: input.componentDir,
|
|
33115
|
+
apiDir: input.apiDir
|
|
32896
33116
|
};
|
|
32897
33117
|
return result;
|
|
32898
33118
|
}
|
|
33119
|
+
function validateAppNotifications(inspection, options) {
|
|
33120
|
+
return validateExtractedAppNotifications({ inspection, extracted: extractAppNotifications(inspection), componentDir: options.componentDir, apiDir: options.apiDir });
|
|
33121
|
+
}
|
|
32899
33122
|
|
|
32900
33123
|
// packages/dbx-cli/validate/src/lib/notification-m-validate-folder/index.ts
|
|
32901
33124
|
var notification_m_validate_folder_exports = {};
|
|
@@ -38012,6 +38235,7 @@ function listAppAssets(inspection, options) {
|
|
|
38012
38235
|
// packages/dbx-cli/validate/src/lib/notification-m-list-app/index.ts
|
|
38013
38236
|
var notification_m_list_app_exports = {};
|
|
38014
38237
|
__export(notification_m_list_app_exports, {
|
|
38238
|
+
collectAppNotifications: () => collectAppNotifications,
|
|
38015
38239
|
formatReportAsJson: () => formatReportAsJson5,
|
|
38016
38240
|
formatReportAsMarkdown: () => formatReportAsMarkdown5,
|
|
38017
38241
|
listAppNotifications: () => listAppNotifications
|
|
@@ -38043,8 +38267,12 @@ function buildTemplateSummaries(extracted) {
|
|
|
38043
38267
|
const record = extracted.templateInfoRecord;
|
|
38044
38268
|
const resolved = new Set(record ? record.resolvedInfoIdentifiers : []);
|
|
38045
38269
|
const handlerByType = /* @__PURE__ */ new Map();
|
|
38270
|
+
const handlerEntriesByType = /* @__PURE__ */ new Map();
|
|
38046
38271
|
for (const entry of extracted.templateHandlerEntries) {
|
|
38047
38272
|
handlerByType.set(entry.typeIdentifier, { factoryFunctionName: entry.factoryFunctionName });
|
|
38273
|
+
const entries = handlerEntriesByType.get(entry.typeIdentifier) ?? [];
|
|
38274
|
+
entries.push(entry);
|
|
38275
|
+
handlerEntriesByType.set(entry.typeIdentifier, entries);
|
|
38048
38276
|
}
|
|
38049
38277
|
const templates = [];
|
|
38050
38278
|
for (const constant of extracted.templateTypeConstants) {
|
|
@@ -38056,8 +38284,13 @@ function buildTemplateSummaries(extracted) {
|
|
|
38056
38284
|
infoSymbolName: info?.symbolName,
|
|
38057
38285
|
humanName: info?.humanName,
|
|
38058
38286
|
description: info?.description,
|
|
38059
|
-
|
|
38287
|
+
notificationModelIdentity: info?.notificationModelIdentity,
|
|
38060
38288
|
targetModelIdentity: info?.targetModelIdentity,
|
|
38289
|
+
userConfigurableDeliveryMethods: info?.userConfigurableDeliveryMethods,
|
|
38290
|
+
userConfigurableDeliveryMethodsSource: info?.userConfigurableDeliveryMethodsSource,
|
|
38291
|
+
forcedDeliveryMethods: info?.forcedDeliveryMethods,
|
|
38292
|
+
forcedDeliveryMethodsSource: info?.forcedDeliveryMethodsSource,
|
|
38293
|
+
factoryContentDeliveryMethods: unionContentDeliveryMethods(handlerEntriesByType.get(constant.symbolName) ?? []),
|
|
38061
38294
|
inInfoRecord: info ? resolved.has(info.symbolName) : false,
|
|
38062
38295
|
hasFactory: handler !== void 0,
|
|
38063
38296
|
factoryFunctionName: handler?.factoryFunctionName,
|
|
@@ -38066,6 +38299,15 @@ function buildTemplateSummaries(extracted) {
|
|
|
38066
38299
|
}
|
|
38067
38300
|
return templates;
|
|
38068
38301
|
}
|
|
38302
|
+
function unionContentDeliveryMethods(entries) {
|
|
38303
|
+
const result = [];
|
|
38304
|
+
for (const entry of entries) {
|
|
38305
|
+
for (const method of entry.contentDeliveryMethods) {
|
|
38306
|
+
if (!result.includes(method)) result.push(method);
|
|
38307
|
+
}
|
|
38308
|
+
}
|
|
38309
|
+
return result;
|
|
38310
|
+
}
|
|
38069
38311
|
function buildTaskSummaries(extracted) {
|
|
38070
38312
|
const indices = buildTaskIndices(extracted);
|
|
38071
38313
|
const tasks = [];
|
|
@@ -38193,13 +38435,45 @@ function formatTemplateBlock(t) {
|
|
|
38193
38435
|
parts.push(heading);
|
|
38194
38436
|
if (t.humanName) parts.push(`- Human name: ${t.humanName}`);
|
|
38195
38437
|
if (t.description) parts.push(`- Description: ${t.description}`);
|
|
38196
|
-
if (t.
|
|
38438
|
+
if (t.notificationModelIdentity) parts.push(`- Notification model: \`${t.notificationModelIdentity}\``);
|
|
38197
38439
|
if (t.targetModelIdentity) parts.push(`- Target model: \`${t.targetModelIdentity}\``);
|
|
38440
|
+
const deliveryMethodsText = formatUserConfigurableDeliveryMethods(t);
|
|
38441
|
+
if (deliveryMethodsText) parts.push(`- User-configurable delivery methods: ${deliveryMethodsText}`);
|
|
38442
|
+
const forcedDeliveryMethodsText = formatForcedDeliveryMethods(t);
|
|
38443
|
+
if (forcedDeliveryMethodsText) parts.push(`- Forced (always on) delivery methods: ${forcedDeliveryMethodsText}`);
|
|
38444
|
+
if (t.factoryContentDeliveryMethods.length > 0) parts.push(`- Factory returns content for: ${t.factoryContentDeliveryMethods.join(", ")}`);
|
|
38198
38445
|
const infoText = t.infoSymbolName ? code6(t.infoSymbolName) : "_Missing._";
|
|
38199
38446
|
const factorySuffix = t.factoryFunctionName ? ` (${code6(t.factoryFunctionName)})` : "";
|
|
38200
38447
|
parts.push(`- Info object: ${infoText}`, `- In info record: ${formatBool2(t.inInfoRecord)}`, `- Has factory: ${formatBool2(t.hasFactory)}${factorySuffix}`, `- Source: \`${t.sourceFile}\``);
|
|
38201
38448
|
return parts.join("\n");
|
|
38202
38449
|
}
|
|
38450
|
+
function formatUserConfigurableDeliveryMethods(t) {
|
|
38451
|
+
let result;
|
|
38452
|
+
switch (t.userConfigurableDeliveryMethodsSource) {
|
|
38453
|
+
case "declared":
|
|
38454
|
+
result = t.userConfigurableDeliveryMethods && t.userConfigurableDeliveryMethods.length > 0 ? t.userConfigurableDeliveryMethods.join(", ") : "_none_";
|
|
38455
|
+
break;
|
|
38456
|
+
case "default":
|
|
38457
|
+
result = t.forcedDeliveryMethodsSource === "declared" && (t.forcedDeliveryMethods ?? []).some((method) => method !== "TEXT") ? "_none_ (the default when the info forces a method)" : "default";
|
|
38458
|
+
break;
|
|
38459
|
+
case "unresolved":
|
|
38460
|
+
result = "_unresolved_";
|
|
38461
|
+
break;
|
|
38462
|
+
case void 0:
|
|
38463
|
+
result = void 0;
|
|
38464
|
+
break;
|
|
38465
|
+
}
|
|
38466
|
+
return result;
|
|
38467
|
+
}
|
|
38468
|
+
function formatForcedDeliveryMethods(t) {
|
|
38469
|
+
let result;
|
|
38470
|
+
if (t.forcedDeliveryMethodsSource === "declared" && t.forcedDeliveryMethods && t.forcedDeliveryMethods.length > 0) {
|
|
38471
|
+
result = t.forcedDeliveryMethods.join(", ");
|
|
38472
|
+
} else if (t.forcedDeliveryMethodsSource === "unresolved") {
|
|
38473
|
+
result = "_unresolved_";
|
|
38474
|
+
}
|
|
38475
|
+
return result;
|
|
38476
|
+
}
|
|
38203
38477
|
function formatTaskBlock(t) {
|
|
38204
38478
|
const parts = [];
|
|
38205
38479
|
const heading = t.typeCode ? `### ${t.typeCode} \u2014 \`${t.symbolName}\`` : `### \`${t.symbolName}\``;
|
|
@@ -44619,6 +44893,8 @@ var DBX_NOTIFICATION_M_VALIDATE_APP_TOOL = {
|
|
|
44619
44893
|
"",
|
|
44620
44894
|
"External identifiers imported from `@dereekb/*` are trusted \u2014 spreads and handler `type:` references that resolve into upstream packages do not produce orphan/unresolved errors.",
|
|
44621
44895
|
"",
|
|
44896
|
+
"The same checks run at build time through `dbx-cli-generate-notification-manifest` (see the `@dereekb/dbx-cli` README); this tool always scans the current source.",
|
|
44897
|
+
"",
|
|
44622
44898
|
"Provide both:",
|
|
44623
44899
|
"- `componentDir`: relative path to the `-firebase` component package (e.g. `components/demo-firebase`).",
|
|
44624
44900
|
"- `apiDir`: relative path to the API app (e.g. `apps/demo-api`).",
|
|
@@ -44655,7 +44931,9 @@ var { formatReportAsJson: formatReportAsJson11, formatReportAsMarkdown: formatRe
|
|
|
44655
44931
|
var DBX_NOTIFICATION_M_LIST_APP_TOOL = {
|
|
44656
44932
|
name: "dbx_notification_m_list_app",
|
|
44657
44933
|
description: [
|
|
44658
|
-
"List every notification template + notification task configured in a downstream `-firebase` component + API app pair. Each entry reports its type code, metadata (human name, description, notification/target model identity), and registration flags \u2014 `inInfoRecord` + `hasFactory` for templates, `inAllArray` + `inValidateList` + `hasHandler` for tasks.",
|
|
44934
|
+
"List every notification template + notification task configured in a downstream `-firebase` component + API app pair. Each entry reports its type code, metadata (human name, description, notification/target model identity), and registration flags \u2014 `inInfoRecord` + `hasFactory` for templates, `inAllArray` + `inValidateList` + `hasHandler` for tasks. Templates also report `userConfigurableDeliveryMethods` (with a `default` / `declared` / `unresolved` source), `forcedDeliveryMethods` (always-on methods, with the same kind of source) and `factoryContentDeliveryMethods`, the channels their factory builds content for.",
|
|
44935
|
+
"",
|
|
44936
|
+
"Its JSON is the report part of the `notification.manifest.json` that `dbx-cli-generate-notification-manifest` writes at build time; this tool always scans the current source.",
|
|
44659
44937
|
"",
|
|
44660
44938
|
"Cross-file resolution mirrors `dbx_notification_m_validate_app` \u2014 spreads and factory composition are traced through every `.ts` file under `src/lib/model/notification/` on the component side and `src/app/common/model/notification/` + `src/app/common/firebase/` on the API side.",
|
|
44661
44939
|
"",
|
|
@@ -51420,7 +51698,7 @@ export const <<SCREAMING>>_NOTIFICATION_TEMPLATE_TYPE_INFO: NotificationTemplate
|
|
|
51420
51698
|
type: <<SCREAMING>>_NOTIFICATION_TEMPLATE_TYPE,
|
|
51421
51699
|
name: '<<Pascal>>',
|
|
51422
51700
|
description: 'TODO: describe the <<Pascal>> notification.',
|
|
51423
|
-
|
|
51701
|
+
notificationModelIdentity: profileIdentity // TODO: replace with the model identity this notification targets
|
|
51424
51702
|
};
|
|
51425
51703
|
|
|
51426
51704
|
export interface <<Pascal>>NotificationData {
|
|
@@ -52075,6 +52353,31 @@ var STORAGEFILE_PROCESSOR_SUBTASK = {
|
|
|
52075
52353
|
seeAlso: ["storagefile-processor", "storagefile-purpose"],
|
|
52076
52354
|
verify: "Run `dbx_validate_app_storagefiles` \u2014 `STORAGEFILE_PROCESSING_SUBTASK_NOT_HANDLED` should clear once the flow entry is added."
|
|
52077
52355
|
};
|
|
52356
|
+
var NOTIFICATION_BUILD_TIME_CHECK_STEP = {
|
|
52357
|
+
heading: "Build-time check",
|
|
52358
|
+
path: "<apiDir>/project.json",
|
|
52359
|
+
body: [
|
|
52360
|
+
"Wire `dbx-cli-generate-notification-manifest` (from `@dereekb/dbx-cli`) as an nx target so broken notification wiring fails the build before deploy. It runs the same rules as `dbx_notification_m_validate_app`, writes `notification.manifest.json`, and exits non-zero on a validator error (warnings are written into the manifest). One target covers every template and task type; add it once per app.",
|
|
52361
|
+
"",
|
|
52362
|
+
"```json",
|
|
52363
|
+
'"generate-notification-manifest": {',
|
|
52364
|
+
' "executor": "nx:run-commands",',
|
|
52365
|
+
' "outputs": ["{workspaceRoot}/dist/<apiDir>/notification.manifest.json"],',
|
|
52366
|
+
' "inputs": [',
|
|
52367
|
+
' "{workspaceRoot}/<componentDir>/src/lib/model/notification/**/*.ts",',
|
|
52368
|
+
' "{workspaceRoot}/<apiDir>/src/app/common/model/notification/**/*.ts",',
|
|
52369
|
+
' "{workspaceRoot}/<apiDir>/src/app/common/firebase/**/*.ts"',
|
|
52370
|
+
" ],",
|
|
52371
|
+
' "options": {',
|
|
52372
|
+
' "command": "npx dbx-cli-generate-notification-manifest --component-dir=<componentDir> --api-dir=<apiDir> --output=dist/<apiDir>/notification.manifest.json",',
|
|
52373
|
+
' "cwd": "{workspaceRoot}"',
|
|
52374
|
+
" }",
|
|
52375
|
+
"}",
|
|
52376
|
+
"```",
|
|
52377
|
+
"",
|
|
52378
|
+
"**Wire into:** the API project's `build.dependsOn` (not `serve`: nothing reads the manifest at runtime, and the server's startup checks already cover `serve`)."
|
|
52379
|
+
].join("\n")
|
|
52380
|
+
};
|
|
52078
52381
|
var NOTIFICATION_TEMPLATE = {
|
|
52079
52382
|
artifact: "notification-template",
|
|
52080
52383
|
title: "Notification template",
|
|
@@ -52087,7 +52390,7 @@ var NOTIFICATION_TEMPLATE = {
|
|
|
52087
52390
|
body: [
|
|
52088
52391
|
"**Required exports:**",
|
|
52089
52392
|
"- `<NAME>_NOTIFICATION_TEMPLATE_TYPE: NotificationTemplateType = '<short_code>'`.",
|
|
52090
|
-
"- `<NAME>_NOTIFICATION_TEMPLATE_TYPE_INFO: NotificationTemplateTypeInfo` with `{ type, name, description,
|
|
52393
|
+
"- `<NAME>_NOTIFICATION_TEMPLATE_TYPE_INFO: NotificationTemplateTypeInfo` with `{ type, name, description, notificationModelIdentity, targetModelIdentity? }`.",
|
|
52091
52394
|
"- For multi-file workspaces, also declare `ALL_<MODULE>_NOTIFICATION_TEMPLATE_TYPE_INFOS: NotificationTemplateTypeInfo[]` and re-export the per-file infos.",
|
|
52092
52395
|
"",
|
|
52093
52396
|
"**Barrel:** Re-export from `<componentDir>/src/lib/model/notification/index.ts`."
|
|
@@ -52109,10 +52412,11 @@ var NOTIFICATION_TEMPLATE = {
|
|
|
52109
52412
|
"",
|
|
52110
52413
|
"**Wire into:** the top-level `<app>NotificationTemplateServiceConfigsArrayFactory(context)` \u2014 call the new factory inside its returned array. The configs-array factory is itself bound via `NOTIFICATION_TEMPLATE_SERVICE_CONFIGS_ARRAY_TOKEN` in the notification module."
|
|
52111
52414
|
].join("\n")
|
|
52112
|
-
}
|
|
52415
|
+
},
|
|
52416
|
+
NOTIFICATION_BUILD_TIME_CHECK_STEP
|
|
52113
52417
|
],
|
|
52114
52418
|
seeAlso: ["notification-task"],
|
|
52115
|
-
verify: "Run `dbx_notification_m_validate_app` to confirm both registration paths (info-record + handler factory) are wired, then `dbx_notification_m_list_app` to confirm the new template appears with `inInfoRecord: true` and `hasFactory: true
|
|
52419
|
+
verify: "Run `dbx_notification_m_validate_app` to confirm both registration paths (info-record + handler factory) are wired, then `dbx_notification_m_list_app` to confirm the new template appears with `inInfoRecord: true` and `hasFactory: true`, then `npx nx run <api-project>:generate-notification-manifest` runs the same checks the build does."
|
|
52116
52420
|
};
|
|
52117
52421
|
var NOTIFICATION_TASK = {
|
|
52118
52422
|
artifact: "notification-task",
|
|
@@ -52152,10 +52456,11 @@ var NOTIFICATION_TASK = {
|
|
|
52152
52456
|
{
|
|
52153
52457
|
heading: "Wire into task service",
|
|
52154
52458
|
body: ["Push the new handler config into the `handlers: [...]` array passed to `notificationTaskService({ validate, handlers })`. The `validate:` spread should already cover it via `ALL_NOTIFICATION_TASK_TYPES`."].join("\n")
|
|
52155
|
-
}
|
|
52459
|
+
},
|
|
52460
|
+
NOTIFICATION_BUILD_TIME_CHECK_STEP
|
|
52156
52461
|
],
|
|
52157
52462
|
seeAlso: ["notification-template"],
|
|
52158
|
-
verify: "Run `dbx_notification_m_validate_app` to confirm `inAllArray: true` + `hasHandler: true` for the new task type."
|
|
52463
|
+
verify: "Run `dbx_notification_m_validate_app` to confirm `inAllArray: true` + `hasHandler: true` for the new task type, then `npx nx run <api-project>:generate-notification-manifest` runs the same checks the build does."
|
|
52159
52464
|
};
|
|
52160
52465
|
var NESTJS_MODEL_MODULE = {
|
|
52161
52466
|
artifact: "nestjs-model-module",
|
|
@@ -93,13 +93,13 @@
|
|
|
93
93
|
{
|
|
94
94
|
"name": "buttonStyle",
|
|
95
95
|
"type": "Maybe<DbxButtonStyle>",
|
|
96
|
-
"description": "",
|
|
96
|
+
"description": "Style of the button. Individual style inputs (e.g. {@link color}, {@link raised}) take precedence.\n\nDeclared as a `model` so a directive composed onto the button (e.g. `dbxRotatingButton`) can set it.",
|
|
97
97
|
"required": false
|
|
98
98
|
},
|
|
99
99
|
{
|
|
100
100
|
"name": "color",
|
|
101
101
|
"type": "Maybe<ThemePalette | DbxColorInput>",
|
|
102
|
-
"description": "",
|
|
102
|
+
"description": "Color of the button. Takes precedence over the {@link buttonStyle} color.\n\nDeclared as a `model` so a directive composed onto the button (e.g. `dbxRotatingButton`) can set it.",
|
|
103
103
|
"required": false
|
|
104
104
|
},
|
|
105
105
|
{
|
|
@@ -854,6 +854,36 @@
|
|
|
854
854
|
"example": "<dbx-progress-spinner-button [config]=\"cfg\" (btnClick)=\"onClick()\"></dbx-progress-spinner-button>",
|
|
855
855
|
"minimalExample": "<dbx-progress-spinner-button [config]=\"cfg\"></dbx-progress-spinner-button>"
|
|
856
856
|
},
|
|
857
|
+
{
|
|
858
|
+
"slug": "rotating-button",
|
|
859
|
+
"category": "button",
|
|
860
|
+
"kind": "directive",
|
|
861
|
+
"selector": "[dbxRotatingButton]",
|
|
862
|
+
"className": "DbxRotatingButtonDirective",
|
|
863
|
+
"module": "@dereekb/dbx-web",
|
|
864
|
+
"description": "Turns a `dbx-button` into a rotating / multi-state button. Each click moves to the next configured state, and the\nstate's icon, text, style and aria-label are set on the button.\n\nUse {@link dbxTristateRotatingButtonConfig} for a three-state / 3-phase (default/on/off) toggle. The directive sets the\nbutton's `icon`, `text`, `buttonStyle` and `ariaLabel`, so configure those through the config rather than on the button.\n\nThe button stops click propagation by default, so a rotating button works inside clickable list rows.",
|
|
865
|
+
"inputs": [
|
|
866
|
+
{
|
|
867
|
+
"name": "dbxRotatingButton",
|
|
868
|
+
"type": "Maybe<DbxRotatingButtonConfig<T>>",
|
|
869
|
+
"description": "",
|
|
870
|
+
"required": false
|
|
871
|
+
},
|
|
872
|
+
{
|
|
873
|
+
"name": "dbxRotatingButtonValue",
|
|
874
|
+
"type": "Maybe<T>",
|
|
875
|
+
"description": "The current value. Two-way bindable with `[(dbxRotatingButtonValue)]`.",
|
|
876
|
+
"required": false
|
|
877
|
+
}
|
|
878
|
+
],
|
|
879
|
+
"outputs": [],
|
|
880
|
+
"relatedSlugs": [
|
|
881
|
+
"button",
|
|
882
|
+
"icon-button"
|
|
883
|
+
],
|
|
884
|
+
"example": "readonly tristateConfig = dbxTristateRotatingButtonConfig({ label: 'Text', defaultValue: false });",
|
|
885
|
+
"minimalExample": "<dbx-button iconOnly [dbxRotatingButton]=\"tristateConfig\" [(dbxRotatingButtonValue)]=\"emailEnabled\"></dbx-button>"
|
|
886
|
+
},
|
|
857
887
|
{
|
|
858
888
|
"slug": "browser-agent-view",
|
|
859
889
|
"category": "misc",
|
|
@@ -874,6 +904,12 @@
|
|
|
874
904
|
"type": "Maybe<boolean>",
|
|
875
905
|
"description": "",
|
|
876
906
|
"required": false
|
|
907
|
+
},
|
|
908
|
+
{
|
|
909
|
+
"name": "overrideKey",
|
|
910
|
+
"type": "Maybe<DbxBrowserAgentViewEntryKey>",
|
|
911
|
+
"description": "Key of the manually selected entry. Can be bound two-way to persist the user's choice.",
|
|
912
|
+
"required": false
|
|
877
913
|
}
|
|
878
914
|
],
|
|
879
915
|
"outputs": [],
|
|
@@ -1033,7 +1069,7 @@
|
|
|
1033
1069
|
"selector": "dbx-prompt-confirm",
|
|
1034
1070
|
"className": "DbxPromptConfirmComponent",
|
|
1035
1071
|
"module": "@dereekb/dbx-web",
|
|
1036
|
-
"description": "Renders a confirmation prompt with customizable title, message, confirm, and cancel buttons.",
|
|
1072
|
+
"description": "Renders a confirmation prompt with customizable title, message, confirm, and cancel buttons.\n\nProjected content shows above the buttons. Content marked with the `buttons` attribute shows in the same row, before the\nconfirm button.",
|
|
1037
1073
|
"inputs": [
|
|
1038
1074
|
{
|
|
1039
1075
|
"name": "config",
|
|
@@ -1055,7 +1091,7 @@
|
|
|
1055
1091
|
"description": ""
|
|
1056
1092
|
}
|
|
1057
1093
|
],
|
|
1058
|
-
"contentProjection": "<ng-content></ng-content>",
|
|
1094
|
+
"contentProjection": "<ng-content></ng-content>; <ng-content select=\"[buttons]\"></ng-content>",
|
|
1059
1095
|
"relatedSlugs": [
|
|
1060
1096
|
"prompt",
|
|
1061
1097
|
"action-confirm"
|
|
@@ -2121,7 +2157,20 @@
|
|
|
2121
2157
|
"className": "DbxColorDirective",
|
|
2122
2158
|
"module": "@dereekb/dbx-web",
|
|
2123
2159
|
"description": "Provides themed color *tokens* to the host element — it never paints a background itself.\n\nAccepts either a {@link DbxThemeColor} string (e.g. `'primary'`, `'success'`) or a {@link DbxColorConfig}\nobject carrying arbitrary CSS color values (any hex, `rgb()`, `hsl()`, `var(...)`, `color-mix(...)`, etc.).\nThe directive sets the `--dbx-bg-color-current` / `--dbx-color-current` (and tone) custom properties and adds the\n`.dbx-color` marker class, leaving the actual painting to either the explicit `.dbx-color-bg` utility on the same\nelement or a colored-surface component's own `.dbx-color`-scoped SCSS (e.g. `<dbx-bar>`, `<dbx-button>`, `<dbx-loading>`,\n`<dbx-icon-tile>`, `<mat-card>`) that reads the inherited tokens.\n\nOptionally set {@link dbxColorTone} (0-100) to control background opacity for a tonal/muted appearance.\nWhen tonal mode is active, the `dbx-color-tonal` CSS class is added and a CSS rule overrides the text\ncolor to the vibrant color (via `--dbx-bg-color-current`).\n\nThe standalone `[dbxColorTone]` input wins over `config.tone` when both are set; the same precedence\napplies to tonal mode.",
|
|
2124
|
-
"inputs": [
|
|
2160
|
+
"inputs": [
|
|
2161
|
+
{
|
|
2162
|
+
"name": "dbxColor",
|
|
2163
|
+
"type": "Maybe<DbxColorInput>",
|
|
2164
|
+
"description": "The color to provide tokens for. Declared as a `model` so a colored-surface component on the same host (e.g.\n`dbx-button`) can push its resolved color into the directive, keeping it the single token provider on the element.",
|
|
2165
|
+
"required": false
|
|
2166
|
+
},
|
|
2167
|
+
{
|
|
2168
|
+
"name": "dbxColorTone",
|
|
2169
|
+
"type": "Maybe<DbxColorTone>",
|
|
2170
|
+
"description": "Background tone level (0-100). When set, the background becomes semi-transparent\nand text color switches to the vibrant theme color for a tonal appearance.\n\nDeclared as a `model` for the same reason as {@link dbxColor}: a colored-surface component on the same host (e.g.\n`dbx-avatar-view`) resolves its tone from its own inputs/context and pushes it here, keeping this directive the\nsingle token provider on the element.",
|
|
2171
|
+
"required": false
|
|
2172
|
+
}
|
|
2173
|
+
],
|
|
2125
2174
|
"outputs": [],
|
|
2126
2175
|
"relatedSlugs": [
|
|
2127
2176
|
"text-color",
|
|
@@ -513,6 +513,125 @@
|
|
|
513
513
|
"derivedComposites": [],
|
|
514
514
|
"derivedFieldOverrides": []
|
|
515
515
|
},
|
|
516
|
+
{
|
|
517
|
+
"slug": "notification-users-with-text-phone-number-query",
|
|
518
|
+
"name": "notificationUsersWithTextPhoneNumberQuery",
|
|
519
|
+
"module": "@dereekb/firebase",
|
|
520
|
+
"subpath": "model/notification/notification.query",
|
|
521
|
+
"signature": "notificationUsersWithTextPhoneNumberQuery(phoneNumber: E164PhoneNumber): FirestoreQueryConstraint[]",
|
|
522
|
+
"description": "Query constraints for finding {@link NotificationUser} documents whose texting number (`gc.t`) is the given phone number.\n\nUsed by the text opt-out sync to find the users a STOP reply came from.",
|
|
523
|
+
"model": "NotificationUser",
|
|
524
|
+
"collection": "nu",
|
|
525
|
+
"isNested": false,
|
|
526
|
+
"scope": "COLLECTION",
|
|
527
|
+
"manual": false,
|
|
528
|
+
"skip": false,
|
|
529
|
+
"category": "lookup",
|
|
530
|
+
"params": [
|
|
531
|
+
{
|
|
532
|
+
"name": "phoneNumber",
|
|
533
|
+
"type": "E164PhoneNumber",
|
|
534
|
+
"description": "- The texting number to match.",
|
|
535
|
+
"optional": false
|
|
536
|
+
}
|
|
537
|
+
],
|
|
538
|
+
"returns": "Array of Firestore query constraints filtering for users with that texting number.",
|
|
539
|
+
"tags": [
|
|
540
|
+
"lookup",
|
|
541
|
+
"notificationuser",
|
|
542
|
+
"notification",
|
|
543
|
+
"users",
|
|
544
|
+
"with",
|
|
545
|
+
"text",
|
|
546
|
+
"phone",
|
|
547
|
+
"number",
|
|
548
|
+
"query",
|
|
549
|
+
"notificationuserswithtextphonenumberquery",
|
|
550
|
+
"user",
|
|
551
|
+
"constraints",
|
|
552
|
+
"finding",
|
|
553
|
+
"link",
|
|
554
|
+
"documents",
|
|
555
|
+
"whose",
|
|
556
|
+
"texting",
|
|
557
|
+
"given",
|
|
558
|
+
"used"
|
|
559
|
+
],
|
|
560
|
+
"constraintSequences": [
|
|
561
|
+
{
|
|
562
|
+
"pathLabel": "all",
|
|
563
|
+
"entries": [
|
|
564
|
+
{
|
|
565
|
+
"kind": "where",
|
|
566
|
+
"fieldPath": "gc.t",
|
|
567
|
+
"operator": "=="
|
|
568
|
+
}
|
|
569
|
+
]
|
|
570
|
+
}
|
|
571
|
+
],
|
|
572
|
+
"derivedComposites": [],
|
|
573
|
+
"derivedFieldOverrides": []
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
"slug": "notification-users-with-stopped-text-phone-number-query",
|
|
577
|
+
"name": "notificationUsersWithStoppedTextPhoneNumberQuery",
|
|
578
|
+
"module": "@dereekb/firebase",
|
|
579
|
+
"subpath": "model/notification/notification.query",
|
|
580
|
+
"signature": "notificationUsersWithStoppedTextPhoneNumberQuery(phoneNumber: E164PhoneNumber): FirestoreQueryConstraint[]",
|
|
581
|
+
"description": "Query constraints for finding {@link NotificationUser} documents that have the given phone number in their stopped numbers (`tso`).\n\nUsed by the text opt-out sync to find the users a START reply turns texts back on for.",
|
|
582
|
+
"model": "NotificationUser",
|
|
583
|
+
"collection": "nu",
|
|
584
|
+
"isNested": false,
|
|
585
|
+
"scope": "COLLECTION",
|
|
586
|
+
"manual": false,
|
|
587
|
+
"skip": false,
|
|
588
|
+
"category": "lookup",
|
|
589
|
+
"params": [
|
|
590
|
+
{
|
|
591
|
+
"name": "phoneNumber",
|
|
592
|
+
"type": "E164PhoneNumber",
|
|
593
|
+
"description": "- The stopped phone number to match.",
|
|
594
|
+
"optional": false
|
|
595
|
+
}
|
|
596
|
+
],
|
|
597
|
+
"returns": "Array of Firestore query constraints filtering for users that stopped that number.",
|
|
598
|
+
"tags": [
|
|
599
|
+
"lookup",
|
|
600
|
+
"notificationuser",
|
|
601
|
+
"notification",
|
|
602
|
+
"users",
|
|
603
|
+
"with",
|
|
604
|
+
"stopped",
|
|
605
|
+
"text",
|
|
606
|
+
"phone",
|
|
607
|
+
"number",
|
|
608
|
+
"query",
|
|
609
|
+
"notificationuserswithstoppedtextphonenumberquery",
|
|
610
|
+
"user",
|
|
611
|
+
"constraints",
|
|
612
|
+
"finding",
|
|
613
|
+
"link",
|
|
614
|
+
"documents",
|
|
615
|
+
"have",
|
|
616
|
+
"given",
|
|
617
|
+
"their",
|
|
618
|
+
"numbers"
|
|
619
|
+
],
|
|
620
|
+
"constraintSequences": [
|
|
621
|
+
{
|
|
622
|
+
"pathLabel": "all",
|
|
623
|
+
"entries": [
|
|
624
|
+
{
|
|
625
|
+
"kind": "where",
|
|
626
|
+
"fieldPath": "tso",
|
|
627
|
+
"operator": "array-contains"
|
|
628
|
+
}
|
|
629
|
+
]
|
|
630
|
+
}
|
|
631
|
+
],
|
|
632
|
+
"derivedComposites": [],
|
|
633
|
+
"derivedFieldOverrides": []
|
|
634
|
+
},
|
|
516
635
|
{
|
|
517
636
|
"slug": "notification-summaries-flagged-for-needs-initialization-query",
|
|
518
637
|
"name": "notificationSummariesFlaggedForNeedsInitializationQuery",
|
|
@@ -298,6 +298,20 @@
|
|
|
298
298
|
],
|
|
299
299
|
"notes": "Names one upload \"slot\" within a FormSpace — the resume, the cover letter, the photo of the ID.\n\nA slot is stored on the uploaded {@link StorageFile} as its `pg` (purposeSubgroup), which is what makes\n\"replace the file in this slot\" the existing `flagPreviousForDelete` behaviour rather than new machinery."
|
|
300
300
|
},
|
|
301
|
+
{
|
|
302
|
+
"name": "NotificationTemplateTypeInfoGroupKey",
|
|
303
|
+
"package": "@dereekb/firebase",
|
|
304
|
+
"module": "src/lib/model/notification/notification.details",
|
|
305
|
+
"kind": "semantic-type",
|
|
306
|
+
"definition": "string",
|
|
307
|
+
"baseType": "string",
|
|
308
|
+
"topics": [
|
|
309
|
+
"identifier",
|
|
310
|
+
"string",
|
|
311
|
+
"dereekb-firebase:notification"
|
|
312
|
+
],
|
|
313
|
+
"notes": "Key of a {@link NotificationTemplateTypeInfoGroup}. Template types whose groups share a key are shown together."
|
|
314
|
+
},
|
|
301
315
|
{
|
|
302
316
|
"name": "NotificationTemplateType",
|
|
303
317
|
"package": "@dereekb/firebase",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/dbx-components-mcp",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.16.0",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"ts-morph": "^21.0.0"
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
|
-
"@dereekb/dbx-cli": "14.
|
|
16
|
-
"@dereekb/util": "14.
|
|
15
|
+
"@dereekb/dbx-cli": "14.16.0",
|
|
16
|
+
"@dereekb/util": "14.16.0"
|
|
17
17
|
}
|
|
18
18
|
}
|