@dereekb/dbx-cli 13.19.0 → 13.21.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/eslint/index.cjs.js +70 -10
- package/eslint/index.esm.js +70 -10
- package/eslint/package.json +3 -3
- package/firebase-api-manifest/package.json +3 -3
- package/generate-firestore-indexes/main.js +2 -2
- package/generate-firestore-indexes/package.json +2 -2
- package/generate-mcp-manifest/package.json +3 -3
- package/generate-route-manifest/main.js +94 -48
- package/generate-route-manifest/package.json +2 -2
- package/index.cjs.js +646 -582
- package/index.esm.js +646 -583
- package/lint-cache/package.json +2 -2
- package/manifest-extract/package.json +2 -2
- package/package.json +12 -6
- package/route/LICENSE +21 -0
- package/route/index.cjs.default.js +1 -0
- package/route/index.cjs.js +18 -0
- package/route/index.cjs.mjs +2 -0
- package/route/index.d.ts +1 -0
- package/route/index.esm.js +1 -0
- package/route/package.json +25 -0
- package/route/src/index.d.ts +11 -0
- package/src/lib/route/route-manifest.d.ts +1 -1
- package/src/lib/route/route-model-tag.d.ts +10 -3
- package/src/lib/route/url-match.d.ts +12 -0
- package/test/package.json +9 -9
package/eslint/index.cjs.js
CHANGED
|
@@ -669,6 +669,13 @@ var LINE_PREFIX_REGEX = /^(\s*\*?\s?)(.*)$/;
|
|
|
669
669
|
* promoted to `<collectionName>/<id>` at runtime via the model identity).
|
|
670
670
|
* - `gb/:id/gbe/{authUid}` — alternating literal / placeholder segments (even
|
|
671
671
|
* count) → `kind: 'key'` (a full FirestoreModelKey for a subcollection model).
|
|
672
|
+
* An odd (id) segment may also be a `{const:<id>}` token for a fixed/singleton
|
|
673
|
+
* id (e.g. `wk/:uid/wkn/{const:0}`); it is normalized to the bare literal in
|
|
674
|
+
* the parsed `keyTemplate` so the runtime emits it verbatim, while a forgotten
|
|
675
|
+
* `:` (a bare `note`) still fails as malformed.
|
|
676
|
+
* - `{flatKey:<param>}` — single token → `kind: 'flatKey'`: the `<param>` URL
|
|
677
|
+
* value IS a whole two-way-flat FirestoreModelKey (`r_<id>_cs_<id>_d_<id>`),
|
|
678
|
+
* un-flattened at runtime. For pages that pack a full key into one URL segment.
|
|
672
679
|
* - (absent, list tag) → `kind: 'list'`.
|
|
673
680
|
*
|
|
674
681
|
* This module is deliberately runtime-dependency-free (no ts-morph): the same
|
|
@@ -677,8 +684,8 @@ var LINE_PREFIX_REGEX = /^(\s*\*?\s?)(.*)$/;
|
|
|
677
684
|
* valid tag is. The ts-morph consumer (`extractComponentRouteModelTags`) lives in
|
|
678
685
|
* `./route-models-extract.ts` and re-exports these symbols for existing importers.
|
|
679
686
|
*/ /**
|
|
680
|
-
* Whether a route-model entry resolves to a promoted id, a full key,
|
|
681
|
-
* keyless list.
|
|
687
|
+
* Whether a route-model entry resolves to a promoted id, a full key, a
|
|
688
|
+
* single-param flattened key, or a keyless list.
|
|
682
689
|
*/ function _array_like_to_array(arr, len) {
|
|
683
690
|
if (len == null || len > arr.length) len = arr.length;
|
|
684
691
|
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
@@ -728,6 +735,14 @@ function _unsupported_iterable_to_array(o, minLen) {
|
|
|
728
735
|
var MODEL_TYPE_RE = RegExp("^[a-zA-Z][a-zA-Z0-9]*$", "u");
|
|
729
736
|
var LITERAL_SEGMENT_RE = RegExp("^[a-zA-Z0-9][a-zA-Z0-9_-]*$", "u");
|
|
730
737
|
var AUTH_UID_PLACEHOLDER = '{authUid}';
|
|
738
|
+
/**
|
|
739
|
+
* Matches a `{const:<id>}` fixed-id token (e.g. `{const:0}`), capturing the
|
|
740
|
+
* literal id. The id obeys the same shape as a literal collection segment.
|
|
741
|
+
*/ var CONST_TOKEN_RE = RegExp("^\\{const:([a-zA-Z0-9][a-zA-Z0-9_-]*)\\}$", "u");
|
|
742
|
+
/**
|
|
743
|
+
* Matches a `{flatKey:<param>}` token (e.g. `{flatKey:region}`), capturing the
|
|
744
|
+
* route param name whose URL value holds a whole two-way-flat FirestoreModelKey.
|
|
745
|
+
*/ var FLAT_KEY_TOKEN_RE = RegExp("^\\{flatKey:([a-zA-Z_][a-zA-Z0-9_]*)\\}$", "u");
|
|
731
746
|
/**
|
|
732
747
|
* The bare `@dbxRouteModel` tag name (without the leading `@`).
|
|
733
748
|
*/ var ROUTE_MODEL_TAG = 'dbxRouteModel';
|
|
@@ -807,7 +822,7 @@ function parseModelTag(tokens, description) {
|
|
|
807
822
|
model: {
|
|
808
823
|
modelType: tokens[0],
|
|
809
824
|
kind: parsedKey.kind,
|
|
810
|
-
keyTemplate:
|
|
825
|
+
keyTemplate: parsedKey.keyTemplate,
|
|
811
826
|
description: description,
|
|
812
827
|
routeParams: parsedKey.routeParams
|
|
813
828
|
}
|
|
@@ -842,17 +857,29 @@ function parseKeyTemplate(keyTemplate) {
|
|
|
842
857
|
return result;
|
|
843
858
|
}
|
|
844
859
|
function parseSingleSegmentKey(segment, keyTemplate) {
|
|
860
|
+
var flatKeyParam = flatKeyTokenParam(segment);
|
|
845
861
|
var placeholder = placeholderParam(segment);
|
|
846
862
|
var result;
|
|
847
|
-
if (
|
|
863
|
+
if (flatKeyParam !== undefined) {
|
|
864
|
+
// The whole key lives in one URL param; the runtime un-flattens it.
|
|
865
|
+
result = {
|
|
866
|
+
ok: true,
|
|
867
|
+
kind: 'flatKey',
|
|
868
|
+
keyTemplate: keyTemplate,
|
|
869
|
+
routeParams: [
|
|
870
|
+
flatKeyParam
|
|
871
|
+
]
|
|
872
|
+
};
|
|
873
|
+
} else if (placeholder === undefined) {
|
|
848
874
|
result = {
|
|
849
875
|
ok: false,
|
|
850
|
-
message: "Single-segment key template `".concat(keyTemplate, "` must be a placeholder (`:param` or `").concat(AUTH_UID_PLACEHOLDER, "`).")
|
|
876
|
+
message: "Single-segment key template `".concat(keyTemplate, "` must be a placeholder (`:param` or `").concat(AUTH_UID_PLACEHOLDER, "`) or a flattened-key token (`{flatKey:<param>}`).")
|
|
851
877
|
};
|
|
852
878
|
} else {
|
|
853
879
|
result = {
|
|
854
880
|
ok: true,
|
|
855
881
|
kind: 'id',
|
|
882
|
+
keyTemplate: keyTemplate,
|
|
856
883
|
routeParams: placeholder.routeParam === undefined ? [] : [
|
|
857
884
|
placeholder.routeParam
|
|
858
885
|
]
|
|
@@ -862,6 +889,10 @@ function parseSingleSegmentKey(segment, keyTemplate) {
|
|
|
862
889
|
}
|
|
863
890
|
function parseAlternatingKey(segments, keyTemplate) {
|
|
864
891
|
var routeParams = [];
|
|
892
|
+
// The normalized template substitutes any `{const:<id>}` token back to its
|
|
893
|
+
// bare literal so the runtime `resolveFullKey` (which emits non-placeholder
|
|
894
|
+
// segments verbatim) round-trips without needing to understand the token.
|
|
895
|
+
var normalizedSegments = [];
|
|
865
896
|
var message;
|
|
866
897
|
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
867
898
|
try {
|
|
@@ -872,14 +903,20 @@ function parseAlternatingKey(segments, keyTemplate) {
|
|
|
872
903
|
message = "Key template `".concat(keyTemplate, "` segment `").concat(segment, "` must be a literal collection name.");
|
|
873
904
|
break;
|
|
874
905
|
}
|
|
906
|
+
normalizedSegments.push(segment);
|
|
875
907
|
} else {
|
|
876
908
|
var placeholder = placeholderParam(segment);
|
|
877
|
-
|
|
878
|
-
|
|
909
|
+
var constId = constTokenId(segment);
|
|
910
|
+
if (placeholder !== undefined) {
|
|
911
|
+
if (placeholder.routeParam !== undefined) {
|
|
912
|
+
routeParams.push(placeholder.routeParam);
|
|
913
|
+
}
|
|
914
|
+
normalizedSegments.push(segment);
|
|
915
|
+
} else if (constId === undefined) {
|
|
916
|
+
message = "Key template `".concat(keyTemplate, "` segment `").concat(segment, "` must be a placeholder (`:param` or `").concat(AUTH_UID_PLACEHOLDER, "`) or a fixed id (`{const:<id>}`).");
|
|
879
917
|
break;
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
routeParams.push(placeholder.routeParam);
|
|
918
|
+
} else {
|
|
919
|
+
normalizedSegments.push(constId);
|
|
883
920
|
}
|
|
884
921
|
}
|
|
885
922
|
}
|
|
@@ -900,6 +937,7 @@ function parseAlternatingKey(segments, keyTemplate) {
|
|
|
900
937
|
return message === undefined ? {
|
|
901
938
|
ok: true,
|
|
902
939
|
kind: 'key',
|
|
940
|
+
keyTemplate: normalizedSegments.join('/'),
|
|
903
941
|
routeParams: routeParams
|
|
904
942
|
} : {
|
|
905
943
|
ok: false,
|
|
@@ -929,6 +967,28 @@ function parseAlternatingKey(segments, keyTemplate) {
|
|
|
929
967
|
}
|
|
930
968
|
return result;
|
|
931
969
|
}
|
|
970
|
+
/**
|
|
971
|
+
* Extracts the literal id from a `{const:<id>}` fixed-id token, used for a
|
|
972
|
+
* fixed/singleton subcollection id at an odd key-template position. Returns
|
|
973
|
+
* `undefined` for any non-`{const:…}` segment.
|
|
974
|
+
*
|
|
975
|
+
* @param segment - The single key-template segment to classify.
|
|
976
|
+
* @returns The captured literal id, or `undefined` when not a const token.
|
|
977
|
+
*/ function constTokenId(segment) {
|
|
978
|
+
var match = CONST_TOKEN_RE.exec(segment);
|
|
979
|
+
return match === null ? undefined : match[1];
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Extracts the route param name from a `{flatKey:<param>}` token, whose URL
|
|
983
|
+
* value is a whole two-way-flat FirestoreModelKey un-flattened at runtime.
|
|
984
|
+
* Returns `undefined` for any non-`{flatKey:…}` segment.
|
|
985
|
+
*
|
|
986
|
+
* @param segment - The single key-template segment to classify.
|
|
987
|
+
* @returns The captured route param name, or `undefined` when not a flatKey token.
|
|
988
|
+
*/ function flatKeyTokenParam(segment) {
|
|
989
|
+
var match = FLAT_KEY_TOKEN_RE.exec(segment);
|
|
990
|
+
return match === null ? undefined : match[1];
|
|
991
|
+
}
|
|
932
992
|
|
|
933
993
|
/**
|
|
934
994
|
* ESLint rule enforcing that `@dbxRouteModel` / `@dbxRouteModelList` tags parse
|
package/eslint/index.esm.js
CHANGED
|
@@ -667,6 +667,13 @@ var LINE_PREFIX_REGEX = /^(\s*\*?\s?)(.*)$/;
|
|
|
667
667
|
* promoted to `<collectionName>/<id>` at runtime via the model identity).
|
|
668
668
|
* - `gb/:id/gbe/{authUid}` — alternating literal / placeholder segments (even
|
|
669
669
|
* count) → `kind: 'key'` (a full FirestoreModelKey for a subcollection model).
|
|
670
|
+
* An odd (id) segment may also be a `{const:<id>}` token for a fixed/singleton
|
|
671
|
+
* id (e.g. `wk/:uid/wkn/{const:0}`); it is normalized to the bare literal in
|
|
672
|
+
* the parsed `keyTemplate` so the runtime emits it verbatim, while a forgotten
|
|
673
|
+
* `:` (a bare `note`) still fails as malformed.
|
|
674
|
+
* - `{flatKey:<param>}` — single token → `kind: 'flatKey'`: the `<param>` URL
|
|
675
|
+
* value IS a whole two-way-flat FirestoreModelKey (`r_<id>_cs_<id>_d_<id>`),
|
|
676
|
+
* un-flattened at runtime. For pages that pack a full key into one URL segment.
|
|
670
677
|
* - (absent, list tag) → `kind: 'list'`.
|
|
671
678
|
*
|
|
672
679
|
* This module is deliberately runtime-dependency-free (no ts-morph): the same
|
|
@@ -675,8 +682,8 @@ var LINE_PREFIX_REGEX = /^(\s*\*?\s?)(.*)$/;
|
|
|
675
682
|
* valid tag is. The ts-morph consumer (`extractComponentRouteModelTags`) lives in
|
|
676
683
|
* `./route-models-extract.ts` and re-exports these symbols for existing importers.
|
|
677
684
|
*/ /**
|
|
678
|
-
* Whether a route-model entry resolves to a promoted id, a full key,
|
|
679
|
-
* keyless list.
|
|
685
|
+
* Whether a route-model entry resolves to a promoted id, a full key, a
|
|
686
|
+
* single-param flattened key, or a keyless list.
|
|
680
687
|
*/ function _array_like_to_array(arr, len) {
|
|
681
688
|
if (len == null || len > arr.length) len = arr.length;
|
|
682
689
|
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
@@ -726,6 +733,14 @@ function _unsupported_iterable_to_array(o, minLen) {
|
|
|
726
733
|
var MODEL_TYPE_RE = RegExp("^[a-zA-Z][a-zA-Z0-9]*$", "u");
|
|
727
734
|
var LITERAL_SEGMENT_RE = RegExp("^[a-zA-Z0-9][a-zA-Z0-9_-]*$", "u");
|
|
728
735
|
var AUTH_UID_PLACEHOLDER = '{authUid}';
|
|
736
|
+
/**
|
|
737
|
+
* Matches a `{const:<id>}` fixed-id token (e.g. `{const:0}`), capturing the
|
|
738
|
+
* literal id. The id obeys the same shape as a literal collection segment.
|
|
739
|
+
*/ var CONST_TOKEN_RE = RegExp("^\\{const:([a-zA-Z0-9][a-zA-Z0-9_-]*)\\}$", "u");
|
|
740
|
+
/**
|
|
741
|
+
* Matches a `{flatKey:<param>}` token (e.g. `{flatKey:region}`), capturing the
|
|
742
|
+
* route param name whose URL value holds a whole two-way-flat FirestoreModelKey.
|
|
743
|
+
*/ var FLAT_KEY_TOKEN_RE = RegExp("^\\{flatKey:([a-zA-Z_][a-zA-Z0-9_]*)\\}$", "u");
|
|
729
744
|
/**
|
|
730
745
|
* The bare `@dbxRouteModel` tag name (without the leading `@`).
|
|
731
746
|
*/ var ROUTE_MODEL_TAG = 'dbxRouteModel';
|
|
@@ -805,7 +820,7 @@ function parseModelTag(tokens, description) {
|
|
|
805
820
|
model: {
|
|
806
821
|
modelType: tokens[0],
|
|
807
822
|
kind: parsedKey.kind,
|
|
808
|
-
keyTemplate:
|
|
823
|
+
keyTemplate: parsedKey.keyTemplate,
|
|
809
824
|
description: description,
|
|
810
825
|
routeParams: parsedKey.routeParams
|
|
811
826
|
}
|
|
@@ -840,17 +855,29 @@ function parseKeyTemplate(keyTemplate) {
|
|
|
840
855
|
return result;
|
|
841
856
|
}
|
|
842
857
|
function parseSingleSegmentKey(segment, keyTemplate) {
|
|
858
|
+
var flatKeyParam = flatKeyTokenParam(segment);
|
|
843
859
|
var placeholder = placeholderParam(segment);
|
|
844
860
|
var result;
|
|
845
|
-
if (
|
|
861
|
+
if (flatKeyParam !== undefined) {
|
|
862
|
+
// The whole key lives in one URL param; the runtime un-flattens it.
|
|
863
|
+
result = {
|
|
864
|
+
ok: true,
|
|
865
|
+
kind: 'flatKey',
|
|
866
|
+
keyTemplate: keyTemplate,
|
|
867
|
+
routeParams: [
|
|
868
|
+
flatKeyParam
|
|
869
|
+
]
|
|
870
|
+
};
|
|
871
|
+
} else if (placeholder === undefined) {
|
|
846
872
|
result = {
|
|
847
873
|
ok: false,
|
|
848
|
-
message: "Single-segment key template `".concat(keyTemplate, "` must be a placeholder (`:param` or `").concat(AUTH_UID_PLACEHOLDER, "`).")
|
|
874
|
+
message: "Single-segment key template `".concat(keyTemplate, "` must be a placeholder (`:param` or `").concat(AUTH_UID_PLACEHOLDER, "`) or a flattened-key token (`{flatKey:<param>}`).")
|
|
849
875
|
};
|
|
850
876
|
} else {
|
|
851
877
|
result = {
|
|
852
878
|
ok: true,
|
|
853
879
|
kind: 'id',
|
|
880
|
+
keyTemplate: keyTemplate,
|
|
854
881
|
routeParams: placeholder.routeParam === undefined ? [] : [
|
|
855
882
|
placeholder.routeParam
|
|
856
883
|
]
|
|
@@ -860,6 +887,10 @@ function parseSingleSegmentKey(segment, keyTemplate) {
|
|
|
860
887
|
}
|
|
861
888
|
function parseAlternatingKey(segments, keyTemplate) {
|
|
862
889
|
var routeParams = [];
|
|
890
|
+
// The normalized template substitutes any `{const:<id>}` token back to its
|
|
891
|
+
// bare literal so the runtime `resolveFullKey` (which emits non-placeholder
|
|
892
|
+
// segments verbatim) round-trips without needing to understand the token.
|
|
893
|
+
var normalizedSegments = [];
|
|
863
894
|
var message;
|
|
864
895
|
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
865
896
|
try {
|
|
@@ -870,14 +901,20 @@ function parseAlternatingKey(segments, keyTemplate) {
|
|
|
870
901
|
message = "Key template `".concat(keyTemplate, "` segment `").concat(segment, "` must be a literal collection name.");
|
|
871
902
|
break;
|
|
872
903
|
}
|
|
904
|
+
normalizedSegments.push(segment);
|
|
873
905
|
} else {
|
|
874
906
|
var placeholder = placeholderParam(segment);
|
|
875
|
-
|
|
876
|
-
|
|
907
|
+
var constId = constTokenId(segment);
|
|
908
|
+
if (placeholder !== undefined) {
|
|
909
|
+
if (placeholder.routeParam !== undefined) {
|
|
910
|
+
routeParams.push(placeholder.routeParam);
|
|
911
|
+
}
|
|
912
|
+
normalizedSegments.push(segment);
|
|
913
|
+
} else if (constId === undefined) {
|
|
914
|
+
message = "Key template `".concat(keyTemplate, "` segment `").concat(segment, "` must be a placeholder (`:param` or `").concat(AUTH_UID_PLACEHOLDER, "`) or a fixed id (`{const:<id>}`).");
|
|
877
915
|
break;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
routeParams.push(placeholder.routeParam);
|
|
916
|
+
} else {
|
|
917
|
+
normalizedSegments.push(constId);
|
|
881
918
|
}
|
|
882
919
|
}
|
|
883
920
|
}
|
|
@@ -898,6 +935,7 @@ function parseAlternatingKey(segments, keyTemplate) {
|
|
|
898
935
|
return message === undefined ? {
|
|
899
936
|
ok: true,
|
|
900
937
|
kind: 'key',
|
|
938
|
+
keyTemplate: normalizedSegments.join('/'),
|
|
901
939
|
routeParams: routeParams
|
|
902
940
|
} : {
|
|
903
941
|
ok: false,
|
|
@@ -927,6 +965,28 @@ function parseAlternatingKey(segments, keyTemplate) {
|
|
|
927
965
|
}
|
|
928
966
|
return result;
|
|
929
967
|
}
|
|
968
|
+
/**
|
|
969
|
+
* Extracts the literal id from a `{const:<id>}` fixed-id token, used for a
|
|
970
|
+
* fixed/singleton subcollection id at an odd key-template position. Returns
|
|
971
|
+
* `undefined` for any non-`{const:…}` segment.
|
|
972
|
+
*
|
|
973
|
+
* @param segment - The single key-template segment to classify.
|
|
974
|
+
* @returns The captured literal id, or `undefined` when not a const token.
|
|
975
|
+
*/ function constTokenId(segment) {
|
|
976
|
+
var match = CONST_TOKEN_RE.exec(segment);
|
|
977
|
+
return match === null ? undefined : match[1];
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Extracts the route param name from a `{flatKey:<param>}` token, whose URL
|
|
981
|
+
* value is a whole two-way-flat FirestoreModelKey un-flattened at runtime.
|
|
982
|
+
* Returns `undefined` for any non-`{flatKey:…}` segment.
|
|
983
|
+
*
|
|
984
|
+
* @param segment - The single key-template segment to classify.
|
|
985
|
+
* @returns The captured route param name, or `undefined` when not a flatKey token.
|
|
986
|
+
*/ function flatKeyTokenParam(segment) {
|
|
987
|
+
var match = FLAT_KEY_TOKEN_RE.exec(segment);
|
|
988
|
+
return match === null ? undefined : match[1];
|
|
989
|
+
}
|
|
930
990
|
|
|
931
991
|
/**
|
|
932
992
|
* ESLint rule enforcing that `@dbxRouteModel` / `@dbxRouteModelList` tags parse
|
package/eslint/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/dbx-cli/eslint",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.21.0",
|
|
4
4
|
"peerDependencies": {
|
|
5
|
-
"@dereekb/dbx-cli": "13.
|
|
6
|
-
"@dereekb/util": "13.
|
|
5
|
+
"@dereekb/dbx-cli": "13.21.0",
|
|
6
|
+
"@dereekb/util": "13.21.0",
|
|
7
7
|
"@typescript-eslint/utils": "8.59.3"
|
|
8
8
|
},
|
|
9
9
|
"devDependencies": {
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/dbx-cli-firebase-api-manifest",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.21.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"devDependencies": {
|
|
7
7
|
"ts-morph": "^21.0.0"
|
|
8
8
|
},
|
|
9
9
|
"peerDependencies": {
|
|
10
|
-
"@dereekb/dbx-cli": "13.
|
|
11
|
-
"@dereekb/util": "13.
|
|
10
|
+
"@dereekb/dbx-cli": "13.21.0",
|
|
11
|
+
"@dereekb/util": "13.21.0",
|
|
12
12
|
"prettier": "3.8.3"
|
|
13
13
|
}
|
|
14
14
|
}
|
|
@@ -5,14 +5,14 @@ const require = __createRequire(import.meta.url);
|
|
|
5
5
|
// packages/dbx-cli/generate-firestore-indexes/package.json
|
|
6
6
|
var package_default = {
|
|
7
7
|
name: "@dereekb/dbx-cli-generate-firestore-indexes",
|
|
8
|
-
version: "13.
|
|
8
|
+
version: "13.21.0",
|
|
9
9
|
private: true,
|
|
10
10
|
type: "module",
|
|
11
11
|
devDependencies: {
|
|
12
12
|
eslint: "10.4.0"
|
|
13
13
|
},
|
|
14
14
|
peerDependencies: {
|
|
15
|
-
"@dereekb/dbx-cli": "13.
|
|
15
|
+
"@dereekb/dbx-cli": "13.21.0"
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
18
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/dbx-cli-generate-firestore-indexes",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.21.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"devDependencies": {
|
|
7
7
|
"eslint": "10.4.0"
|
|
8
8
|
},
|
|
9
9
|
"peerDependencies": {
|
|
10
|
-
"@dereekb/dbx-cli": "13.
|
|
10
|
+
"@dereekb/dbx-cli": "13.21.0"
|
|
11
11
|
}
|
|
12
12
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/dbx-cli-generate-mcp-manifest",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.21.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"peerDependencies": {
|
|
7
|
-
"@dereekb/dbx-cli": "13.
|
|
8
|
-
"@dereekb/model": "13.
|
|
7
|
+
"@dereekb/dbx-cli": "13.21.0",
|
|
8
|
+
"@dereekb/model": "13.21.0",
|
|
9
9
|
"arktype": "^2.2.0",
|
|
10
10
|
"jiti": "2.6.1"
|
|
11
11
|
}
|
|
@@ -334,6 +334,42 @@ function collectRelativeImports(sourceFile, fileName) {
|
|
|
334
334
|
return out;
|
|
335
335
|
}
|
|
336
336
|
|
|
337
|
+
// packages/dbx-cli/src/lib/route/url-match.ts
|
|
338
|
+
function stripUrlQueryAndHash(url) {
|
|
339
|
+
const hashStripped = url.split("#", 1)[0];
|
|
340
|
+
return hashStripped.split("?", 1)[0];
|
|
341
|
+
}
|
|
342
|
+
function extractUrlParamKeys(fullUrl) {
|
|
343
|
+
if (fullUrl === void 0 || fullUrl.length === 0) {
|
|
344
|
+
return [];
|
|
345
|
+
}
|
|
346
|
+
const seen = /* @__PURE__ */ new Set();
|
|
347
|
+
const keys = [];
|
|
348
|
+
for (const segment of fullUrl.split("/")) {
|
|
349
|
+
const key = extractParamKeyFromSegment(segment);
|
|
350
|
+
if (key !== void 0 && !seen.has(key)) {
|
|
351
|
+
seen.add(key);
|
|
352
|
+
keys.push(key);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return keys;
|
|
356
|
+
}
|
|
357
|
+
function extractParamKeyFromSegment(rawSegment) {
|
|
358
|
+
const segment = stripUrlQueryAndHash(rawSegment);
|
|
359
|
+
if (segment.startsWith(":")) {
|
|
360
|
+
const key = segment.slice(1);
|
|
361
|
+
return key.length > 0 ? key : void 0;
|
|
362
|
+
}
|
|
363
|
+
if (segment.startsWith("{") && segment.endsWith("}")) {
|
|
364
|
+
const inner = segment.slice(1, -1);
|
|
365
|
+
const colonIdx = inner.indexOf(":");
|
|
366
|
+
const rawKey = colonIdx >= 0 ? inner.slice(0, colonIdx) : inner;
|
|
367
|
+
const key = rawKey.trim();
|
|
368
|
+
return key.length > 0 ? key : void 0;
|
|
369
|
+
}
|
|
370
|
+
return void 0;
|
|
371
|
+
}
|
|
372
|
+
|
|
337
373
|
// packages/dbx-cli/src/lib/route/route-build-tree.ts
|
|
338
374
|
// @__NO_SIDE_EFFECTS__
|
|
339
375
|
function buildRouteTree(nodes, extractIssues) {
|
|
@@ -493,7 +529,7 @@ function composeFullUrl(node) {
|
|
|
493
529
|
return void 0;
|
|
494
530
|
}
|
|
495
531
|
const joined = segments.join("");
|
|
496
|
-
const collapsed = joined.replaceAll(/\/{2,}/g, "/");
|
|
532
|
+
const collapsed = stripUrlQueryAndHash(joined).replaceAll(/\/{2,}/g, "/");
|
|
497
533
|
return collapsed.length === 0 ? "/" : collapsed;
|
|
498
534
|
}
|
|
499
535
|
function compareByName(a, b) {
|
|
@@ -541,37 +577,6 @@ function loadRouteTree(args) {
|
|
|
541
577
|
return result;
|
|
542
578
|
}
|
|
543
579
|
|
|
544
|
-
// packages/dbx-cli/src/lib/route/url-match.ts
|
|
545
|
-
function extractUrlParamKeys(fullUrl) {
|
|
546
|
-
if (fullUrl === void 0 || fullUrl.length === 0) {
|
|
547
|
-
return [];
|
|
548
|
-
}
|
|
549
|
-
const seen = /* @__PURE__ */ new Set();
|
|
550
|
-
const keys = [];
|
|
551
|
-
for (const segment of fullUrl.split("/")) {
|
|
552
|
-
const key = extractParamKeyFromSegment(segment);
|
|
553
|
-
if (key !== void 0 && !seen.has(key)) {
|
|
554
|
-
seen.add(key);
|
|
555
|
-
keys.push(key);
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
return keys;
|
|
559
|
-
}
|
|
560
|
-
function extractParamKeyFromSegment(segment) {
|
|
561
|
-
if (segment.startsWith(":")) {
|
|
562
|
-
const key = segment.slice(1);
|
|
563
|
-
return key.length > 0 ? key : void 0;
|
|
564
|
-
}
|
|
565
|
-
if (segment.startsWith("{") && segment.endsWith("}")) {
|
|
566
|
-
const inner = segment.slice(1, -1);
|
|
567
|
-
const colonIdx = inner.indexOf(":");
|
|
568
|
-
const rawKey = colonIdx >= 0 ? inner.slice(0, colonIdx) : inner;
|
|
569
|
-
const key = rawKey.trim();
|
|
570
|
-
return key.length > 0 ? key : void 0;
|
|
571
|
-
}
|
|
572
|
-
return void 0;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
580
|
// packages/dbx-cli/src/lib/route/component-resolve.ts
|
|
576
581
|
import { dirname, join, normalize } from "node:path/posix";
|
|
577
582
|
var TRY_EXTENSIONS = [".ts", ".tsx", "/index.ts", "/index.tsx"];
|
|
@@ -646,6 +651,8 @@ function containsImportedSymbol(importStatement, symbol) {
|
|
|
646
651
|
var MODEL_TYPE_RE = /^[a-zA-Z][a-zA-Z0-9]*$/u;
|
|
647
652
|
var LITERAL_SEGMENT_RE = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/u;
|
|
648
653
|
var AUTH_UID_PLACEHOLDER = "{authUid}";
|
|
654
|
+
var CONST_TOKEN_RE = /^\{const:([a-zA-Z0-9][a-zA-Z0-9_-]*)\}$/u;
|
|
655
|
+
var FLAT_KEY_TOKEN_RE = /^\{flatKey:([a-zA-Z_][a-zA-Z0-9_]*)\}$/u;
|
|
649
656
|
var ROUTE_MODEL_TAG = "dbxRouteModel";
|
|
650
657
|
var ROUTE_MODEL_LIST_TAG = "dbxRouteModelList";
|
|
651
658
|
function parseRouteModelTag(tag) {
|
|
@@ -681,7 +688,7 @@ function parseModelTag(tokens, description) {
|
|
|
681
688
|
} else if (MODEL_TYPE_RE.test(tokens[0])) {
|
|
682
689
|
const parsedKey = parseKeyTemplate(tokens[1]);
|
|
683
690
|
if (parsedKey.ok) {
|
|
684
|
-
result = { ok: true, model: { modelType: tokens[0], kind: parsedKey.kind, keyTemplate:
|
|
691
|
+
result = { ok: true, model: { modelType: tokens[0], kind: parsedKey.kind, keyTemplate: parsedKey.keyTemplate, description, routeParams: parsedKey.routeParams } };
|
|
685
692
|
} else {
|
|
686
693
|
result = { ok: false, message: parsedKey.message };
|
|
687
694
|
}
|
|
@@ -703,17 +710,21 @@ function parseKeyTemplate(keyTemplate) {
|
|
|
703
710
|
return result;
|
|
704
711
|
}
|
|
705
712
|
function parseSingleSegmentKey(segment, keyTemplate) {
|
|
713
|
+
const flatKeyParam = flatKeyTokenParam(segment);
|
|
706
714
|
const placeholder = placeholderParam(segment);
|
|
707
715
|
let result;
|
|
708
|
-
if (
|
|
709
|
-
result = { ok:
|
|
716
|
+
if (flatKeyParam !== void 0) {
|
|
717
|
+
result = { ok: true, kind: "flatKey", keyTemplate, routeParams: [flatKeyParam] };
|
|
718
|
+
} else if (placeholder === void 0) {
|
|
719
|
+
result = { ok: false, message: `Single-segment key template \`${keyTemplate}\` must be a placeholder (\`:param\` or \`${AUTH_UID_PLACEHOLDER}\`) or a flattened-key token (\`{flatKey:<param>}\`).` };
|
|
710
720
|
} else {
|
|
711
|
-
result = { ok: true, kind: "id", routeParams: placeholder.routeParam === void 0 ? [] : [placeholder.routeParam] };
|
|
721
|
+
result = { ok: true, kind: "id", keyTemplate, routeParams: placeholder.routeParam === void 0 ? [] : [placeholder.routeParam] };
|
|
712
722
|
}
|
|
713
723
|
return result;
|
|
714
724
|
}
|
|
715
725
|
function parseAlternatingKey(segments, keyTemplate) {
|
|
716
726
|
const routeParams = [];
|
|
727
|
+
const normalizedSegments = [];
|
|
717
728
|
let message;
|
|
718
729
|
for (const [i, segment] of segments.entries()) {
|
|
719
730
|
if (i % 2 === 0) {
|
|
@@ -721,18 +732,24 @@ function parseAlternatingKey(segments, keyTemplate) {
|
|
|
721
732
|
message = `Key template \`${keyTemplate}\` segment \`${segment}\` must be a literal collection name.`;
|
|
722
733
|
break;
|
|
723
734
|
}
|
|
735
|
+
normalizedSegments.push(segment);
|
|
724
736
|
} else {
|
|
725
737
|
const placeholder = placeholderParam(segment);
|
|
726
|
-
|
|
727
|
-
|
|
738
|
+
const constId = constTokenId(segment);
|
|
739
|
+
if (placeholder !== void 0) {
|
|
740
|
+
if (placeholder.routeParam !== void 0) {
|
|
741
|
+
routeParams.push(placeholder.routeParam);
|
|
742
|
+
}
|
|
743
|
+
normalizedSegments.push(segment);
|
|
744
|
+
} else if (constId === void 0) {
|
|
745
|
+
message = `Key template \`${keyTemplate}\` segment \`${segment}\` must be a placeholder (\`:param\` or \`${AUTH_UID_PLACEHOLDER}\`) or a fixed id (\`{const:<id>}\`).`;
|
|
728
746
|
break;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
routeParams.push(placeholder.routeParam);
|
|
747
|
+
} else {
|
|
748
|
+
normalizedSegments.push(constId);
|
|
732
749
|
}
|
|
733
750
|
}
|
|
734
751
|
}
|
|
735
|
-
return message === void 0 ? { ok: true, kind: "key", routeParams } : { ok: false, message };
|
|
752
|
+
return message === void 0 ? { ok: true, kind: "key", keyTemplate: normalizedSegments.join("/"), routeParams } : { ok: false, message };
|
|
736
753
|
}
|
|
737
754
|
function placeholderParam(segment) {
|
|
738
755
|
let result;
|
|
@@ -745,6 +762,14 @@ function placeholderParam(segment) {
|
|
|
745
762
|
}
|
|
746
763
|
return result;
|
|
747
764
|
}
|
|
765
|
+
function constTokenId(segment) {
|
|
766
|
+
const match = CONST_TOKEN_RE.exec(segment);
|
|
767
|
+
return match === null ? void 0 : match[1];
|
|
768
|
+
}
|
|
769
|
+
function flatKeyTokenParam(segment) {
|
|
770
|
+
const match = FLAT_KEY_TOKEN_RE.exec(segment);
|
|
771
|
+
return match === null ? void 0 : match[1];
|
|
772
|
+
}
|
|
748
773
|
|
|
749
774
|
// packages/dbx-cli/src/lib/route/route-models-extract.ts
|
|
750
775
|
function extractComponentRouteModelTags(sourceFile, component) {
|
|
@@ -766,7 +791,7 @@ function collectRouteModelTags2(jsDocs) {
|
|
|
766
791
|
|
|
767
792
|
// packages/dbx-cli/src/lib/route/route-manifest.ts
|
|
768
793
|
import { Project as Project2 } from "ts-morph";
|
|
769
|
-
var ROUTE_MANIFEST_VERSION =
|
|
794
|
+
var ROUTE_MANIFEST_VERSION = 2;
|
|
770
795
|
function buildRouteManifest(input, now = /* @__PURE__ */ new Date()) {
|
|
771
796
|
const warnings = [];
|
|
772
797
|
const tree = loadRouteTree({ sources: input.sources });
|
|
@@ -990,7 +1015,12 @@ function formatRouteManifestWarning(warning) {
|
|
|
990
1015
|
return `[generate-route-manifest] ${warning.severity}: ${warning.kind}: ${warning.message}`;
|
|
991
1016
|
}
|
|
992
1017
|
function countRouteManifestGenerationErrors(input) {
|
|
993
|
-
|
|
1018
|
+
const allow = new Set(input.allowWarning ?? []);
|
|
1019
|
+
const errorCount = input.warnings.filter((warning) => warning.severity === "error").length;
|
|
1020
|
+
const blockableWarnings = input.warnings.filter((warning) => warning.severity === "warning" && !allow.has(warning.kind));
|
|
1021
|
+
const exceedsMax = input.maxWarnings !== void 0 && blockableWarnings.length > input.maxWarnings;
|
|
1022
|
+
const blockingWarningCount = input.strict || exceedsMax ? blockableWarnings.length : 0;
|
|
1023
|
+
return errorCount + blockingWarningCount;
|
|
994
1024
|
}
|
|
995
1025
|
function extractModelTypesFromModelsInput(parsed) {
|
|
996
1026
|
const models = parsed?.models;
|
|
@@ -1030,9 +1060,10 @@ async function main() {
|
|
|
1030
1060
|
console.error(`generate-route-manifest: 0 states extracted from ${describePatterns(flags.src)}; not writing ${relative(WORKSPACE_ROOT, outputPath)}.`);
|
|
1031
1061
|
process.exit(1);
|
|
1032
1062
|
}
|
|
1033
|
-
const errorCount = countRouteManifestGenerationErrors({ warnings, strict: flags.strict });
|
|
1063
|
+
const errorCount = countRouteManifestGenerationErrors({ warnings, strict: flags.strict, allowWarning: flags.allowWarning, ...flags.maxWarnings == null ? {} : { maxWarnings: flags.maxWarnings } });
|
|
1034
1064
|
if (errorCount > 0) {
|
|
1035
|
-
|
|
1065
|
+
const gate = flags.strict ? " (--strict)" : flags.maxWarnings == null ? "" : ` (--max-warnings=${flags.maxWarnings})`;
|
|
1066
|
+
console.error(`generate-route-manifest: ${errorCount} blocking issue(s)${gate}; not writing ${relative(WORKSPACE_ROOT, outputPath)}.`);
|
|
1036
1067
|
process.exit(1);
|
|
1037
1068
|
}
|
|
1038
1069
|
const serialized = `${JSON.stringify(manifest, null, 2)}
|
|
@@ -1085,11 +1116,13 @@ function resolveWorkspacePath(value) {
|
|
|
1085
1116
|
}
|
|
1086
1117
|
function parseFlags(argv) {
|
|
1087
1118
|
const src = [];
|
|
1119
|
+
const allowWarning = [];
|
|
1088
1120
|
let app;
|
|
1089
1121
|
let baseUrl;
|
|
1090
1122
|
let output;
|
|
1091
1123
|
let modelsInput;
|
|
1092
1124
|
let strict = false;
|
|
1125
|
+
let maxWarnings;
|
|
1093
1126
|
for (const arg of argv) {
|
|
1094
1127
|
if (arg.startsWith("--src=")) {
|
|
1095
1128
|
src.push(arg.slice("--src=".length));
|
|
@@ -1101,11 +1134,19 @@ function parseFlags(argv) {
|
|
|
1101
1134
|
output = arg.slice("--output=".length);
|
|
1102
1135
|
} else if (arg.startsWith("--models-input=")) {
|
|
1103
1136
|
modelsInput = arg.slice("--models-input=".length);
|
|
1137
|
+
} else if (arg.startsWith("--allow-warning=")) {
|
|
1138
|
+
const kind = arg.slice("--allow-warning=".length).trim();
|
|
1139
|
+
if (kind.length > 0) {
|
|
1140
|
+
allowWarning.push(kind);
|
|
1141
|
+
}
|
|
1142
|
+
} else if (arg.startsWith("--max-warnings=")) {
|
|
1143
|
+
const parsed = Number.parseInt(arg.slice("--max-warnings=".length), 10);
|
|
1144
|
+
maxWarnings = Number.isNaN(parsed) ? void 0 : Math.max(0, parsed);
|
|
1104
1145
|
} else if (arg === "--strict") {
|
|
1105
1146
|
strict = true;
|
|
1106
1147
|
}
|
|
1107
1148
|
}
|
|
1108
|
-
return { src, app, baseUrl, output, modelsInput, strict };
|
|
1149
|
+
return { src, app, baseUrl, output, modelsInput, strict, allowWarning, maxWarnings };
|
|
1109
1150
|
}
|
|
1110
1151
|
function printUsageAndExit() {
|
|
1111
1152
|
console.error(String.raw`generate-route-manifest
|
|
@@ -1126,7 +1167,12 @@ Optional:
|
|
|
1126
1167
|
--models-input=<path> MCP manifest JSON whose models[].modelType seed the
|
|
1127
1168
|
unknown-model-type validation.
|
|
1128
1169
|
--strict Promote all warnings to errors for the exit decision
|
|
1129
|
-
(any finding then fails generation)
|
|
1170
|
+
(any finding then fails generation).
|
|
1171
|
+
--allow-warning=<kind> Tolerate a warning kind (repeatable); it never fails generation,
|
|
1172
|
+
even under --strict / --max-warnings. Kinds: unknown-route-param,
|
|
1173
|
+
unknown-model-type, duplicate-route-model, dropped-future-state,
|
|
1174
|
+
missing-route-model. (malformed-tag is an error and cannot be allowed.)
|
|
1175
|
+
--max-warnings=<N> Fail when non-allowlisted warnings exceed N (0 = fail on any new warning).`);
|
|
1130
1176
|
process.exit(1);
|
|
1131
1177
|
}
|
|
1132
1178
|
try {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/dbx-cli-generate-route-manifest",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.21.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"peerDependencies": {
|
|
7
|
-
"@dereekb/dbx-cli": "13.
|
|
7
|
+
"@dereekb/dbx-cli": "13.21.0",
|
|
8
8
|
"ts-morph": "^21.0.0"
|
|
9
9
|
}
|
|
10
10
|
}
|