@better-fullstack/types 2.3.1 → 2.5.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/LICENSE +22 -0
- package/dist/{defaults-Cl16mM84.mjs → defaults-BzWJP6p0.mjs} +3 -1
- package/dist/defaults-BzWJP6p0.mjs.map +1 -0
- package/dist/{defaults-BCprHCB6.d.mts → defaults-CbLlpynl.d.mts} +2 -2
- package/dist/defaults-CbLlpynl.d.mts.map +1 -0
- package/dist/defaults.d.mts +3 -3
- package/dist/defaults.mjs +1 -1
- package/dist/index.d.mts +6 -6
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +69 -5
- package/dist/index.mjs.map +1 -1
- package/dist/json-schema.d.mts +518 -20
- package/dist/json-schema.d.mts.map +1 -1
- package/dist/json-schema.mjs +1 -1
- package/dist/{schemas-B79HRN5e.d.mts → schemas-BRAokgPa.d.mts} +520 -27
- package/dist/schemas-BRAokgPa.d.mts.map +1 -0
- package/dist/{schemas-CprYz5Sh.mjs → schemas-CtieNRfz.mjs} +74 -3
- package/dist/schemas-CtieNRfz.mjs.map +1 -0
- package/dist/schemas.d.mts +2 -2
- package/dist/schemas.mjs +2 -2
- package/dist/{stack-graph-BkHZlyKH.d.mts → stack-graph-BPw9KsNm.d.mts} +3 -3
- package/dist/stack-graph-BPw9KsNm.d.mts.map +1 -0
- package/dist/{stack-graph-DiLi3WuL.mjs → stack-graph-CXTiPofQ.mjs} +205 -27
- package/dist/stack-graph-CXTiPofQ.mjs.map +1 -0
- package/dist/stack-graph.d.mts +3 -3
- package/dist/stack-graph.mjs +2 -2
- package/dist/{stack-translation-Ay-_1mNB.mjs → stack-translation-B-zArJLM.mjs} +433 -29
- package/dist/stack-translation-B-zArJLM.mjs.map +1 -0
- package/dist/{stack-translation-BmsX82IA.d.mts → stack-translation-D7KkQm8W.d.mts} +30 -9
- package/dist/stack-translation-D7KkQm8W.d.mts.map +1 -0
- package/dist/stack-translation.d.mts +4 -4
- package/dist/stack-translation.mjs +3 -3
- package/dist/{types-BuRzqsRV.d.mts → types-Br0NsnBr.d.mts} +10 -3
- package/dist/types-Br0NsnBr.d.mts.map +1 -0
- package/dist/types.d.mts +3 -3
- package/package.json +3 -2
- package/dist/defaults-BCprHCB6.d.mts.map +0 -1
- package/dist/defaults-Cl16mM84.mjs.map +0 -1
- package/dist/schemas-B79HRN5e.d.mts.map +0 -1
- package/dist/schemas-CprYz5Sh.mjs.map +0 -1
- package/dist/stack-graph-BkHZlyKH.d.mts.map +0 -1
- package/dist/stack-graph-DiLi3WuL.mjs.map +0 -1
- package/dist/stack-translation-Ay-_1mNB.mjs.map +0 -1
- package/dist/stack-translation-BmsX82IA.d.mts.map +0 -1
- package/dist/types-BuRzqsRV.d.mts.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $
|
|
1
|
+
import { $i as RUST_CACHING_VALUES, A as CSS_FRAMEWORK_VALUES, An as GO_OBSERVABILITY_VALUES, At as ELIXIR_OBSERVABILITY_VALUES, B as DOTNET_DEPLOY_VALUES, Br as MOBILE_TESTING_VALUES, Cn as GO_CACHING_VALUES, Ct as ELIXIR_EMAIL_VALUES, Dn as GO_LOGGING_VALUES, Dt as ELIXIR_JOBS_VALUES, En as GO_DI_VALUES, Et as ELIXIR_I18N_VALUES, F as DATABASE_VALUES, Fn as GO_TEMPLATING_VALUES, Fr as MOBILE_LIBRARIES_VALUES, Ft as ELIXIR_VALIDATION_VALUES, G as DOTNET_ORM_VALUES, H as DOTNET_JOB_QUEUE_VALUES, In as GO_TESTING_VALUES, Ir as MOBILE_NAVIGATION_VALUES, It as ELIXIR_WEB_FRAMEWORK_VALUES, J as DOTNET_VALIDATION_VALUES, Ji as RATE_LIMIT_VALUES, K as DOTNET_REALTIME_VALUES, L as DOTNET_API_VALUES, La as STATE_MANAGEMENT_VALUES, Ln as GO_VALIDATION_VALUES, Lr as MOBILE_OTA_VALUES, Lt as EMAIL_VALUES, Mn as GO_PROTO_TOOLING_VALUES, Mr as LOGGING_VALUES, Mt as ELIXIR_QUALITY_VALUES, N as DART_MOBILE_VALUES, Nn as GO_QUALITY_VALUES, Nt as ELIXIR_REALTIME_VALUES, O as CMS_VALUES, Oa as SEARCH_VALUES, On as GO_MESSAGE_QUEUE_VALUES, Or as KOTLIN_MOBILE_LIBRARIES_VALUES, P as DATABASE_SETUP_VALUES, Pn as GO_REALTIME_VALUES, Pr as MOBILE_DEEP_LINKING_VALUES, Pt as ELIXIR_TESTING_VALUES, Qi as RUST_AUTH_VALUES, Qr as ORM_VALUES, R as DOTNET_AUTH_VALUES, Ra as SWIFT_MOBILE_VALUES, Rn as GO_WEB_FRAMEWORK_VALUES, Rr as MOBILE_PUSH_VALUES, Rt as EXAMPLES_VALUES, Si as PYTHON_WEB_FRAMEWORK_VALUES, Sn as GO_AUTH_VALUES, St as ELIXIR_DOCUMENTATION_VALUES, T as CACHING_VALUES, Tn as GO_CONFIG_VALUES, Tt as ELIXIR_HTTP_VALUES, U as DOTNET_LIBRARIES_VALUES, V as DOTNET_FRONTEND_VALUES, Vr as MOBILE_UI_VALUES, W as DOTNET_OBSERVABILITY_VALUES, Xi as RUNTIME_VALUES, Xr as OBSERVABILITY_VALUES, Y as DOTNET_WEB_FRAMEWORK_VALUES, Ya as StackPartRoleSchema, Yi as REALTIME_VALUES, Zi as RUST_API_VALUES, _i as PYTHON_REALTIME_VALUES, _r as JOB_QUEUE_VALUES, _t as ELIXIR_APPLICATION_FRAMEWORK_VALUES, a as ANALYTICS_VALUES, aa as RUST_MESSAGE_QUEUE_VALUES, ai as PYTHON_CACHING_VALUES, ao as UI_LIBRARY_VALUES, ar as INTEGRATIONS_VALUES, bi as PYTHON_TESTING_VALUES, bt as ELIXIR_CLUSTERING_VALUES, c as API_VALUES, ca as RUST_REALTIME_VALUES, ci as PYTHON_DATA_VALUES, cr as JAVA_API_VALUES, d as AUTH_VALUES, di as PYTHON_MEDIA_VALUES, dn as FEATURE_FLAGS_VALUES, dr as JAVA_LANGUAGE_VALUES, ea as RUST_CLI_VALUES, fi as PYTHON_MESSAGE_QUEUE_VALUES, fn as FILE_STORAGE_VALUES, fo as WEB_DEPLOY_VALUES, fr as JAVA_LIBRARIES_VALUES, gi as PYTHON_QUALITY_VALUES, gr as JAVA_WEB_FRAMEWORK_VALUES, gt as ELIXIR_API_VALUES, hi as PYTHON_PACKAGE_MANAGER_VALUES, hn as FRONTEND_VALUES, hr as JAVA_TESTING_LIBRARIES_VALUES, ht as EFFECT_VALUES, i as AI_VALUES, ia as RUST_LOGGING_VALUES, ii as PYTHON_AUTH_VALUES, jn as GO_ORM_VALUES, jt as ELIXIR_ORM_VALUES, ka as SERVER_DEPLOY_VALUES, kn as GO_MIGRATIONS_VALUES, kr as KOTLIN_MOBILE_VALUES, kt as ELIXIR_LIBRARIES_VALUES, l as APP_PLATFORM_ADDON_VALUES, la as RUST_TEMPLATING_VALUES, li as PYTHON_GRAPHQL_VALUES, lr as JAVA_AUTH_VALUES, mi as PYTHON_ORM_VALUES, mn as FORMS_VALUES, mr as JAVA_ORM_VALUES, na as RUST_FRONTEND_VALUES, ni as PYTHON_AI_VALUES, o as ANIMATION_VALUES, oa as RUST_OBSERVABILITY_VALUES, oi as PYTHON_CLI_VALUES, oo as VALIDATION_VALUES, pi as PYTHON_OBSERVABILITY_VALUES, pn as FILE_UPLOAD_VALUES, pr as JAVA_LOGGING_VALUES, pt as ECOMMERCE_VALUES, q as DOTNET_TESTING_VALUES, ra as RUST_LIBRARIES_VALUES, ri as PYTHON_API_VALUES, rr as I18N_VALUES, sa as RUST_ORM_VALUES, si as PYTHON_CLOUD_SDK_VALUES, so as VECTOR_DB_VALUES, t as ADDONS_VALUES, ta as RUST_ERROR_HANDLING_VALUES, ti as PAYMENTS_VALUES, to as TESTING_VALUES, ua as RUST_WEB_FRAMEWORK_VALUES, ui as PYTHON_HTTP_CLIENT_VALUES, ur as JAVA_BUILD_TOOL_VALUES, vi as PYTHON_SERVER_VALUES, vt as ELIXIR_AUTH_VALUES, wn as GO_CLI_VALUES, wt as ELIXIR_HTTP_SERVER_VALUES, xi as PYTHON_VALIDATION_VALUES, xn as GO_API_VALUES, xt as ELIXIR_DEPLOY_VALUES, y as BACKEND_VALUES, yi as PYTHON_TASK_QUEUE_VALUES, yt as ELIXIR_CACHING_VALUES, z as DOTNET_CACHING_VALUES, zr as MOBILE_STORAGE_VALUES } from "./schemas-CtieNRfz.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/stack-compatibility-rules.ts
|
|
4
4
|
const WEB_FRAMEWORKS = [
|
|
@@ -534,6 +534,8 @@ const LEGACY_TYPESCRIPT_BACKEND_SINGLE_CATEGORIES = {
|
|
|
534
534
|
rateLimit: "rateLimit",
|
|
535
535
|
fileStorage: "fileStorage",
|
|
536
536
|
featureFlags: "featureFlags",
|
|
537
|
+
integrations: "integrations",
|
|
538
|
+
ecommerce: "ecommerce",
|
|
537
539
|
payments: "payments",
|
|
538
540
|
realtime: "realtime",
|
|
539
541
|
ai: "ai",
|
|
@@ -559,6 +561,8 @@ const LEGACY_TYPESCRIPT_FRONTEND_SINGLE_CATEGORIES = {
|
|
|
559
561
|
};
|
|
560
562
|
const LEGACY_DATABASE_SINGLE_CATEGORIES = { dbSetup: "dbSetup" };
|
|
561
563
|
const LEGACY_MOBILE_SINGLE_CATEGORIES = {
|
|
564
|
+
auth: "auth",
|
|
565
|
+
payments: "payments",
|
|
562
566
|
navigation: "mobileNavigation",
|
|
563
567
|
ui: "mobileUI",
|
|
564
568
|
storage: "mobileStorage",
|
|
@@ -595,8 +599,10 @@ const CODE_QUALITY_ADDONS = new Set([
|
|
|
595
599
|
"oxlint",
|
|
596
600
|
"ultracite",
|
|
597
601
|
"lefthook",
|
|
598
|
-
"husky"
|
|
602
|
+
"husky",
|
|
603
|
+
"gitleaks"
|
|
599
604
|
]);
|
|
605
|
+
const TYPESCRIPT_CODE_QUALITY_ADDONS = new Set(["knip"]);
|
|
600
606
|
const DOCUMENTATION_ADDONS = new Set(["starlight", "fumadocs"]);
|
|
601
607
|
const FRONTEND_APP_PLATFORM_ADDONS = new Set(APP_PLATFORM_ADDON_VALUES);
|
|
602
608
|
const FRONTEND_DATA_FETCHING_ADDONS = new Set([
|
|
@@ -616,6 +622,7 @@ const WORKSPACE_TOOLING_ADDONS = new Set([
|
|
|
616
622
|
"nx",
|
|
617
623
|
"docker-compose",
|
|
618
624
|
"devcontainer",
|
|
625
|
+
"kong",
|
|
619
626
|
"github-actions",
|
|
620
627
|
"ruler",
|
|
621
628
|
"mcp",
|
|
@@ -649,6 +656,7 @@ const LEGACY_ARRAY_CATEGORIES = new Set([
|
|
|
649
656
|
"javaLibraries",
|
|
650
657
|
"javaTestingLibraries",
|
|
651
658
|
"mobileLibraries",
|
|
659
|
+
"kotlinMobileLibraries",
|
|
652
660
|
"dotnetLibraries",
|
|
653
661
|
"dotnetTesting",
|
|
654
662
|
"dotnetObservability",
|
|
@@ -659,6 +667,10 @@ const LEGACY_ARRAY_CATEGORIES = new Set([
|
|
|
659
667
|
"elixirLibraries"
|
|
660
668
|
]);
|
|
661
669
|
function getAddonStackPartBinding(toolId) {
|
|
670
|
+
if (TYPESCRIPT_CODE_QUALITY_ADDONS.has(toolId)) return {
|
|
671
|
+
role: "codeQuality",
|
|
672
|
+
ecosystem: "typescript"
|
|
673
|
+
};
|
|
662
674
|
if (CODE_QUALITY_ADDONS.has(toolId)) return {
|
|
663
675
|
role: "codeQuality",
|
|
664
676
|
ecosystem: "universal"
|
|
@@ -702,6 +714,12 @@ const OWNER_ROLES_BY_SCOPED_ROLE = {
|
|
|
702
714
|
...Object.fromEntries(Object.keys(LEGACY_TYPESCRIPT_FRONTEND_SINGLE_CATEGORIES).map((role) => [role, ["frontend"]])),
|
|
703
715
|
...Object.fromEntries([...Object.keys(LEGACY_MOBILE_SINGLE_CATEGORIES), ...Object.keys(LEGACY_MOBILE_ARRAY_CATEGORIES)].map((role) => [role, ["mobile"]])),
|
|
704
716
|
deploy: ["frontend", "backend"],
|
|
717
|
+
auth: [
|
|
718
|
+
"frontend",
|
|
719
|
+
"backend",
|
|
720
|
+
"mobile"
|
|
721
|
+
],
|
|
722
|
+
payments: ["backend", "mobile"],
|
|
705
723
|
dbSetup: ["database"],
|
|
706
724
|
migrations: ["backend"],
|
|
707
725
|
ui: ["frontend", "mobile"],
|
|
@@ -905,6 +923,7 @@ const GRAPH_PROJECTION_DEFAULT_LEGACY_CATEGORIES = [
|
|
|
905
923
|
...Object.values(LEGACY_DATABASE_SINGLE_CATEGORIES),
|
|
906
924
|
...Object.values(LEGACY_MOBILE_SINGLE_CATEGORIES),
|
|
907
925
|
...Object.values(LEGACY_MOBILE_ARRAY_CATEGORIES),
|
|
926
|
+
"kotlinMobileLibraries",
|
|
908
927
|
...Object.values(LEGACY_EXTRA_CATEGORIES_BY_ECOSYSTEM).flatMap((categories) => Object.values(categories)),
|
|
909
928
|
...Object.values(LEGACY_SHARED_BACKEND_SINGLE_CATEGORIES_BY_ECOSYSTEM).flatMap((categories) => Object.values(categories))
|
|
910
929
|
];
|
|
@@ -946,6 +965,10 @@ const STACK_TOOL_DEFINITIONS = [
|
|
|
946
965
|
allowMultiple: true,
|
|
947
966
|
ownerless: true
|
|
948
967
|
}),
|
|
968
|
+
...defineTools(ADDONS_VALUES.filter((value) => TYPESCRIPT_CODE_QUALITY_ADDONS.has(value)), "codeQuality", "typescript", void 0, {
|
|
969
|
+
allowMultiple: true,
|
|
970
|
+
ownerless: true
|
|
971
|
+
}),
|
|
949
972
|
...defineTools(ADDONS_VALUES.filter((value) => DOCUMENTATION_ADDONS.has(value)), "documentation", "universal", void 0, {
|
|
950
973
|
allowMultiple: true,
|
|
951
974
|
ownerless: true
|
|
@@ -973,6 +996,8 @@ const STACK_TOOL_DEFINITIONS = [
|
|
|
973
996
|
...defineTools(RATE_LIMIT_VALUES, "rateLimit", "typescript", "rateLimit"),
|
|
974
997
|
...defineTools(FILE_STORAGE_VALUES, "fileStorage", "typescript", "fileStorage"),
|
|
975
998
|
...defineTools(FEATURE_FLAGS_VALUES, "featureFlags", "typescript", "featureFlags"),
|
|
999
|
+
...defineTools(INTEGRATIONS_VALUES, "integrations", "typescript", "integrations"),
|
|
1000
|
+
...defineTools(ECOMMERCE_VALUES, "ecommerce", "typescript", "ecommerce"),
|
|
976
1001
|
...defineTools(PAYMENTS_VALUES, "payments", "typescript", "payments"),
|
|
977
1002
|
...defineTools(REALTIME_VALUES, "realtime", "typescript", "realtime"),
|
|
978
1003
|
...defineTools(AI_VALUES, "ai", "typescript", "ai"),
|
|
@@ -981,6 +1006,7 @@ const STACK_TOOL_DEFINITIONS = [
|
|
|
981
1006
|
...defineTools(EFFECT_VALUES, "effect", "typescript", "effect"),
|
|
982
1007
|
...defineTools(TESTING_VALUES, "testing", "typescript", "testing"),
|
|
983
1008
|
...defineTools(AUTH_VALUES, "auth", "react-native", "auth"),
|
|
1009
|
+
...defineTools(["revenuecat"], "payments", "react-native", "payments"),
|
|
984
1010
|
...defineTools(MOBILE_NAVIGATION_VALUES, "navigation", "react-native", "mobileNavigation"),
|
|
985
1011
|
...defineTools(MOBILE_UI_VALUES, "ui", "react-native", "mobileUI"),
|
|
986
1012
|
...defineTools(MOBILE_STORAGE_VALUES, "storage", "react-native", "mobileStorage"),
|
|
@@ -991,6 +1017,11 @@ const STACK_TOOL_DEFINITIONS = [
|
|
|
991
1017
|
...defineTools(MOBILE_LIBRARIES_VALUES, "libraries", "react-native", "mobileLibraries", { allowMultiple: true }),
|
|
992
1018
|
...defineTools(RUST_WEB_FRAMEWORK_VALUES, "backend", "rust", "rustWebFramework"),
|
|
993
1019
|
...defineTools(RUST_FRONTEND_VALUES, "frontend", "rust", "rustFrontend"),
|
|
1020
|
+
...defineTools(DOTNET_FRONTEND_VALUES, "frontend", "dotnet", "dotnetFrontend"),
|
|
1021
|
+
...defineTools(KOTLIN_MOBILE_VALUES, "mobile", "kotlin", "kotlinMobile"),
|
|
1022
|
+
...defineTools(KOTLIN_MOBILE_LIBRARIES_VALUES, "libraries", "kotlin", "kotlinMobileLibraries", { allowMultiple: true }),
|
|
1023
|
+
...defineTools(SWIFT_MOBILE_VALUES, "mobile", "swift", "swiftMobile"),
|
|
1024
|
+
...defineTools(DART_MOBILE_VALUES, "mobile", "dart", "dartMobile"),
|
|
994
1025
|
...defineTools(RUST_ORM_VALUES, "orm", "rust", "rustOrm"),
|
|
995
1026
|
...defineTools(RUST_API_VALUES, "api", "rust", "rustApi"),
|
|
996
1027
|
...defineTools(RUST_AUTH_VALUES, "auth", "rust", "rustAuth"),
|
|
@@ -1321,6 +1352,13 @@ function createTypeScriptBackendCompatibilityIssue(part, context) {
|
|
|
1321
1352
|
message: "Dodo Payments are not yet supported for React + Vite projects."
|
|
1322
1353
|
});
|
|
1323
1354
|
}
|
|
1355
|
+
if (part.role === "payments" && part.toolId === "xendit" && (context.ownerToolId === "none" || context.ownerToolId === "convex")) return createStackGraphIssue({
|
|
1356
|
+
code: "INCOMPATIBLE_OWNER_TOOL",
|
|
1357
|
+
partId: part.id,
|
|
1358
|
+
role: part.role,
|
|
1359
|
+
toolId: part.toolId,
|
|
1360
|
+
message: "Xendit Payment Sessions require a server backend."
|
|
1361
|
+
});
|
|
1324
1362
|
if (part.role === "email" && part.toolId !== "none" && context.ownerToolId === "convex") return createStackGraphIssue({
|
|
1325
1363
|
code: "INCOMPATIBLE_OWNER_TOOL",
|
|
1326
1364
|
partId: part.id,
|
|
@@ -1335,6 +1373,31 @@ function createTypeScriptBackendCompatibilityIssue(part, context) {
|
|
|
1335
1373
|
toolId: part.toolId,
|
|
1336
1374
|
message: "Rate limiting helpers are not generated with Convex backend"
|
|
1337
1375
|
});
|
|
1376
|
+
if (part.role === "integrations" && part.toolId === "nango") {
|
|
1377
|
+
if (context.ownerToolId === "none") return createStackGraphIssue({
|
|
1378
|
+
code: "INCOMPATIBLE_OWNER_TOOL",
|
|
1379
|
+
partId: part.id,
|
|
1380
|
+
role: part.role,
|
|
1381
|
+
toolId: part.toolId,
|
|
1382
|
+
message: "Nango integrations require a generated backend."
|
|
1383
|
+
});
|
|
1384
|
+
if (context.ownerToolId === "convex") return createStackGraphIssue({
|
|
1385
|
+
code: "INCOMPATIBLE_OWNER_TOOL",
|
|
1386
|
+
partId: part.id,
|
|
1387
|
+
role: part.role,
|
|
1388
|
+
toolId: part.toolId,
|
|
1389
|
+
message: "Nango integrations are not available with the Convex backend."
|
|
1390
|
+
});
|
|
1391
|
+
const runtimeTool = context.siblingToolIdsByRole?.runtime;
|
|
1392
|
+
const deployTools = context.selectedToolIdsByRoleList?.deploy ?? [];
|
|
1393
|
+
if (runtimeTool === "workers" || context.ownerToolId === "self" && deployTools.includes("cloudflare")) return createStackGraphIssue({
|
|
1394
|
+
code: "INCOMPATIBLE_GRAPH_SELECTION",
|
|
1395
|
+
partId: part.id,
|
|
1396
|
+
role: part.role,
|
|
1397
|
+
toolId: part.toolId,
|
|
1398
|
+
message: "Nango's Node SDK is not available on Cloudflare Workers."
|
|
1399
|
+
});
|
|
1400
|
+
}
|
|
1338
1401
|
if (part.role === "cms" && part.toolId === "payload") {
|
|
1339
1402
|
if (context.primaryToolIdsByRole?.frontend !== "next") return createStackGraphIssue({
|
|
1340
1403
|
code: "INCOMPATIBLE_GRAPH_SELECTION",
|
|
@@ -1692,8 +1755,8 @@ function createAddonCompatibilityIssue(part, context) {
|
|
|
1692
1755
|
toolId: part.toolId,
|
|
1693
1756
|
message: "Choose either Turborepo or Nx, not both"
|
|
1694
1757
|
});
|
|
1695
|
-
if (part.toolId === "docker-compose" || part.toolId === "devcontainer") {
|
|
1696
|
-
const title = part.toolId === "devcontainer" ? "DevContainer" : "Docker Compose";
|
|
1758
|
+
if (part.toolId === "docker-compose" || part.toolId === "devcontainer" || part.toolId === "kong") {
|
|
1759
|
+
const title = part.toolId === "devcontainer" ? "DevContainer" : part.toolId === "kong" ? "Kong Gateway" : "Docker Compose";
|
|
1697
1760
|
const databaseTool = context.primaryToolIdsByRole?.database;
|
|
1698
1761
|
const primaryEcosystem = backendEcosystem ?? frontendEcosystem ?? context.settings?.ecosystem;
|
|
1699
1762
|
const hasCompatibleEcosystem = [
|
|
@@ -1715,6 +1778,27 @@ function createAddonCompatibilityIssue(part, context) {
|
|
|
1715
1778
|
toolId: part.toolId,
|
|
1716
1779
|
message: `${title} is not compatible with Cloudflare Workers runtime.`
|
|
1717
1780
|
});
|
|
1781
|
+
if (backendEcosystem && !DOCKER_COMPOSE_COMPATIBLE_ECOSYSTEMS.has(backendEcosystem)) return createStackGraphIssue({
|
|
1782
|
+
code: "INCOMPATIBLE_GRAPH_SELECTION",
|
|
1783
|
+
partId: part.id,
|
|
1784
|
+
role: part.role,
|
|
1785
|
+
toolId: part.toolId,
|
|
1786
|
+
message: `${title} does not yet provide a container template for ${backendEcosystem} backends.`
|
|
1787
|
+
});
|
|
1788
|
+
if (part.toolId === "kong" && primaryEcosystem === "typescript" && (!backendTool || backendTool === "none")) return createStackGraphIssue({
|
|
1789
|
+
code: "INCOMPATIBLE_GRAPH_SELECTION",
|
|
1790
|
+
partId: part.id,
|
|
1791
|
+
role: part.role,
|
|
1792
|
+
toolId: part.toolId,
|
|
1793
|
+
message: "Kong Gateway requires a TypeScript backend service."
|
|
1794
|
+
});
|
|
1795
|
+
if (part.toolId === "kong" && primaryEcosystem !== "typescript" && primaryEcosystem !== "java" && (!backendTool || backendTool === "none")) return createStackGraphIssue({
|
|
1796
|
+
code: "INCOMPATIBLE_GRAPH_SELECTION",
|
|
1797
|
+
partId: part.id,
|
|
1798
|
+
role: part.role,
|
|
1799
|
+
toolId: part.toolId,
|
|
1800
|
+
message: `Kong Gateway requires a ${primaryEcosystem ?? "supported"} HTTP server.`
|
|
1801
|
+
});
|
|
1718
1802
|
if (!hasCompatibleEcosystem) return createStackGraphIssue({
|
|
1719
1803
|
code: "INCOMPATIBLE_GRAPH_SELECTION",
|
|
1720
1804
|
partId: part.id,
|
|
@@ -1729,12 +1813,45 @@ function createAddonCompatibilityIssue(part, context) {
|
|
|
1729
1813
|
toolId: part.toolId,
|
|
1730
1814
|
message: `${title} currently supports Next.js, TanStack Router, React Router, React Vite, Solid, or Astro.`
|
|
1731
1815
|
});
|
|
1732
|
-
if (backendEcosystem === "typescript" && backendTool === "self" && frontendTool !== "next") return createStackGraphIssue({
|
|
1816
|
+
if (backendEcosystem === "typescript" && backendTool === "self" && frontendTool !== "next" && frontendTool !== "vinext") return createStackGraphIssue({
|
|
1817
|
+
code: "INCOMPATIBLE_GRAPH_SELECTION",
|
|
1818
|
+
partId: part.id,
|
|
1819
|
+
role: part.role,
|
|
1820
|
+
toolId: part.toolId,
|
|
1821
|
+
message: `${title} self-backend support currently requires Next.js or Vinext.`
|
|
1822
|
+
});
|
|
1823
|
+
if (part.toolId === "kong" && primaryEcosystem === "rust" && (apiTool === "tonic" || apiTool === "jsonrpsee")) return createStackGraphIssue({
|
|
1824
|
+
code: "INCOMPATIBLE_GRAPH_SELECTION",
|
|
1825
|
+
partId: part.id,
|
|
1826
|
+
role: part.role,
|
|
1827
|
+
toolId: part.toolId,
|
|
1828
|
+
message: "Kong Gateway currently requires an HTTP Rust API."
|
|
1829
|
+
});
|
|
1830
|
+
if (part.toolId === "kong" && primaryEcosystem === "rust" && backendTool === "loco") return createStackGraphIssue({
|
|
1831
|
+
code: "INCOMPATIBLE_GRAPH_SELECTION",
|
|
1832
|
+
partId: part.id,
|
|
1833
|
+
role: part.role,
|
|
1834
|
+
toolId: part.toolId,
|
|
1835
|
+
message: "Kong Gateway does not yet support Loco's container configuration."
|
|
1836
|
+
});
|
|
1837
|
+
if (part.toolId === "kong" && primaryEcosystem === "go" && apiTool !== void 0 && [
|
|
1838
|
+
"connect-go",
|
|
1839
|
+
"grpc-gateway",
|
|
1840
|
+
"oapi-codegen",
|
|
1841
|
+
"grpc-go"
|
|
1842
|
+
].includes(apiTool)) return createStackGraphIssue({
|
|
1843
|
+
code: "INCOMPATIBLE_GRAPH_SELECTION",
|
|
1844
|
+
partId: part.id,
|
|
1845
|
+
role: part.role,
|
|
1846
|
+
toolId: part.toolId,
|
|
1847
|
+
message: "Kong Gateway currently requires the primary Go HTTP server API."
|
|
1848
|
+
});
|
|
1849
|
+
if (part.toolId === "kong" && primaryEcosystem === "java" && apiTool === "grpc") return createStackGraphIssue({
|
|
1733
1850
|
code: "INCOMPATIBLE_GRAPH_SELECTION",
|
|
1734
1851
|
partId: part.id,
|
|
1735
1852
|
role: part.role,
|
|
1736
1853
|
toolId: part.toolId,
|
|
1737
|
-
message:
|
|
1854
|
+
message: "Kong Gateway currently requires the primary Java HTTP API."
|
|
1738
1855
|
});
|
|
1739
1856
|
if (frontendEcosystem === "rust" && frontendTool && frontendTool !== "none") return createStackGraphIssue({
|
|
1740
1857
|
code: "INCOMPATIBLE_GRAPH_SELECTION",
|
|
@@ -1750,12 +1867,12 @@ function createAddonCompatibilityIssue(part, context) {
|
|
|
1750
1867
|
toolId: part.toolId,
|
|
1751
1868
|
message: `${title} for Java currently requires Spring Boot.`
|
|
1752
1869
|
});
|
|
1753
|
-
if (primaryEcosystem === "python" && databaseTool && databaseTool !== "none" && databaseTool !== "sqlite" && databaseTool !== "postgres") return createStackGraphIssue({
|
|
1870
|
+
if (primaryEcosystem === "python" && databaseTool && databaseTool !== "none" && databaseTool !== "sqlite" && databaseTool !== "postgres" && databaseTool !== "mongodb") return createStackGraphIssue({
|
|
1754
1871
|
code: "INCOMPATIBLE_GRAPH_SELECTION",
|
|
1755
1872
|
partId: part.id,
|
|
1756
1873
|
role: part.role,
|
|
1757
1874
|
toolId: part.toolId,
|
|
1758
|
-
message: `${title} for Python ORM projects currently supports SQLite
|
|
1875
|
+
message: `${title} for Python ORM projects currently supports SQLite, Postgres, or MongoDB.`
|
|
1759
1876
|
});
|
|
1760
1877
|
}
|
|
1761
1878
|
if (part.toolId === "backend-utils") {
|
|
@@ -2114,40 +2231,55 @@ function parseRolePath(rolePath) {
|
|
|
2114
2231
|
const rawRole = segments.length === 1 ? segments[0] : segments[segments.length - 1];
|
|
2115
2232
|
const rawOwnerRole = segments.length > 1 ? segments[0] : void 0;
|
|
2116
2233
|
const role = StackPartRoleSchema.parse(rawRole);
|
|
2117
|
-
|
|
2118
|
-
|
|
2234
|
+
if (!rawOwnerRole) return { role };
|
|
2235
|
+
const parsedOwnerRole = StackPartRoleSchema.safeParse(rawOwnerRole);
|
|
2236
|
+
if (parsedOwnerRole.success && PRIMARY_ROLES.has(parsedOwnerRole.data)) return {
|
|
2237
|
+
role,
|
|
2238
|
+
ownerRole: parsedOwnerRole.data
|
|
2239
|
+
};
|
|
2119
2240
|
return {
|
|
2120
2241
|
role,
|
|
2121
|
-
|
|
2242
|
+
ownerPartId: sanitizePartId(rawOwnerRole)
|
|
2122
2243
|
};
|
|
2123
2244
|
}
|
|
2124
2245
|
function parseStackPartSpecs(specs, source = "selected") {
|
|
2125
2246
|
const unresolved = specs.map((spec) => {
|
|
2126
2247
|
const [rolePath, ecosystem, toolId, customId] = spec.split(":");
|
|
2127
2248
|
if (!rolePath || !ecosystem || !toolId || spec.split(":").length > 4) throw new Error(`Invalid --part '${spec}'. Use role:ecosystem:tool or owner.role:ecosystem:tool.`);
|
|
2128
|
-
const { role, ownerRole } = parseRolePath(rolePath);
|
|
2249
|
+
const { role, ownerRole, ownerPartId } = parseRolePath(rolePath);
|
|
2129
2250
|
return {
|
|
2130
2251
|
role,
|
|
2131
2252
|
ownerRole,
|
|
2253
|
+
ownerPartId,
|
|
2132
2254
|
ecosystem,
|
|
2133
2255
|
toolId,
|
|
2134
2256
|
customId
|
|
2135
2257
|
};
|
|
2136
2258
|
});
|
|
2137
|
-
const
|
|
2259
|
+
const unresolvedPrimaryParts = unresolved.filter((part) => !part.ownerRole && !part.ownerPartId);
|
|
2260
|
+
const primaryRoleCounts = /* @__PURE__ */ new Map();
|
|
2261
|
+
for (const part of unresolvedPrimaryParts) primaryRoleCounts.set(part.role, (primaryRoleCounts.get(part.role) ?? 0) + 1);
|
|
2262
|
+
const repeatedTargetRoot = {
|
|
2263
|
+
frontend: "apps",
|
|
2264
|
+
backend: "services",
|
|
2265
|
+
mobile: "apps",
|
|
2266
|
+
database: "data"
|
|
2267
|
+
};
|
|
2268
|
+
const primaryParts = unresolvedPrimaryParts.map((part) => createStackPart({
|
|
2138
2269
|
role: part.role,
|
|
2139
2270
|
ecosystem: part.ecosystem,
|
|
2140
2271
|
toolId: part.toolId,
|
|
2141
2272
|
source,
|
|
2142
|
-
id: part.customId
|
|
2273
|
+
id: part.customId,
|
|
2274
|
+
targetPath: (primaryRoleCounts.get(part.role) ?? 0) > 1 && PRIMARY_ROLES.has(part.role) ? `${repeatedTargetRoot[part.role]}/${sanitizePartId(part.customId ?? `${part.ecosystem}-${part.toolId}`).replaceAll(":", "-")}` : void 0
|
|
2143
2275
|
}));
|
|
2144
2276
|
const primaryByRole = new Map(primaryParts.map((part) => [part.role, part]));
|
|
2145
2277
|
const primaryByRoleAndEcosystem = new Map(primaryParts.map((part) => [`${part.role}:${part.ecosystem}`, part]));
|
|
2146
|
-
const scopedParts = unresolved.filter((part) => part.ownerRole !== void 0).map((part) => createStackPart({
|
|
2278
|
+
const scopedParts = unresolved.filter((part) => part.ownerRole !== void 0 || part.ownerPartId !== void 0).map((part) => createStackPart({
|
|
2147
2279
|
role: part.role,
|
|
2148
2280
|
ecosystem: part.ecosystem,
|
|
2149
2281
|
toolId: part.toolId,
|
|
2150
|
-
ownerPartId: primaryByRoleAndEcosystem.get(`${part.ownerRole}:${part.ecosystem}`)?.id ?? primaryByRole.get(part.ownerRole)?.id,
|
|
2282
|
+
ownerPartId: part.ownerPartId ? primaryParts.find((candidate) => candidate.id === part.ownerPartId)?.id ?? part.ownerPartId : part.ownerRole ? primaryByRoleAndEcosystem.get(`${part.ownerRole}:${part.ecosystem}`)?.id ?? primaryByRole.get(part.ownerRole)?.id : void 0,
|
|
2151
2283
|
source,
|
|
2152
2284
|
id: part.customId
|
|
2153
2285
|
}));
|
|
@@ -2155,7 +2287,12 @@ function parseStackPartSpecs(specs, source = "selected") {
|
|
|
2155
2287
|
}
|
|
2156
2288
|
function formatStackPartSpec(part, parts = []) {
|
|
2157
2289
|
const owner = part.ownerPartId ? parts.find((candidate) => candidate.id === part.ownerPartId) : void 0;
|
|
2158
|
-
|
|
2290
|
+
const sameRoleOwners = owner ? parts.filter((candidate) => candidate.role === owner.role && candidate.ecosystem === owner.ecosystem && !candidate.ownerPartId && candidate.source !== "provided") : [];
|
|
2291
|
+
const ownerSelector = owner && sameRoleOwners.length > 1 ? owner.id : owner?.role;
|
|
2292
|
+
const rolePath = ownerSelector ? `${ownerSelector}.${part.role}` : part.role;
|
|
2293
|
+
const canonicalId = sanitizePartId(`${part.role}:${part.ecosystem}:${part.toolId}`);
|
|
2294
|
+
const customId = !part.ownerPartId && part.id !== canonicalId ? `:${part.id}` : "";
|
|
2295
|
+
return `${rolePath}:${part.ecosystem}:${part.toolId}${customId}`;
|
|
2159
2296
|
}
|
|
2160
2297
|
function materializeProvidedStackParts(parts) {
|
|
2161
2298
|
const next = [...parts];
|
|
@@ -2287,6 +2424,11 @@ function legacyProjectConfigToStackParts(config, source = "legacy") {
|
|
|
2287
2424
|
const frontendPart = addLegacyPart(parts, "frontend", "typescript", webFrontend, source);
|
|
2288
2425
|
if (frontendPart?.toolId === "astro" && config.astroIntegration && config.astroIntegration !== "none") frontendPart.settings = { astroIntegration: config.astroIntegration };
|
|
2289
2426
|
const mobilePart = addLegacyPart(parts, "mobile", "react-native", nativeFrontend, source);
|
|
2427
|
+
addLegacyPart(parts, "frontend", "dotnet", config.dotnetFrontend, source);
|
|
2428
|
+
const kotlinMobilePart = addLegacyPart(parts, "mobile", "kotlin", config.kotlinMobile, source);
|
|
2429
|
+
addLegacyPart(parts, "mobile", "swift", config.swiftMobile, source);
|
|
2430
|
+
addLegacyPart(parts, "mobile", "dart", config.dartMobile, source);
|
|
2431
|
+
if (kotlinMobilePart) for (const library of config.kotlinMobileLibraries ?? []) addLegacyPart(parts, "libraries", "kotlin", library, source, kotlinMobilePart.id);
|
|
2290
2432
|
let backendPart;
|
|
2291
2433
|
if ((config.ecosystem === void 0 || config.ecosystem === "typescript") && config.backend && config.backend !== "none") backendPart = addLegacyPart(parts, "backend", "typescript", config.backend, source);
|
|
2292
2434
|
if (config.ecosystem === "rust" && config.rustFrontend && config.rustFrontend !== "none") addLegacyPart(parts, "frontend", "rust", config.rustFrontend, source);
|
|
@@ -2348,10 +2490,10 @@ function legacyProjectConfigToStackParts(config, source = "legacy") {
|
|
|
2348
2490
|
}
|
|
2349
2491
|
function ecosystemForLegacy(parts) {
|
|
2350
2492
|
const frontend = getPrimaryPart(parts, "frontend");
|
|
2351
|
-
if (frontend?.ecosystem && frontend.ecosystem !== "universal") return frontend.ecosystem;
|
|
2493
|
+
if (frontend?.ecosystem && frontend.ecosystem !== "universal" && frontend.ecosystem !== "kotlin" && frontend.ecosystem !== "swift" && frontend.ecosystem !== "dart") return frontend.ecosystem;
|
|
2352
2494
|
if (getPrimaryPart(parts, "mobile")) return "react-native";
|
|
2353
2495
|
const backend = getPrimaryPart(parts, "backend");
|
|
2354
|
-
if (backend?.ecosystem && backend.ecosystem !== "universal") return backend.ecosystem;
|
|
2496
|
+
if (backend?.ecosystem && backend.ecosystem !== "universal" && backend.ecosystem !== "kotlin" && backend.ecosystem !== "swift" && backend.ecosystem !== "dart") return backend.ecosystem;
|
|
2355
2497
|
return "typescript";
|
|
2356
2498
|
}
|
|
2357
2499
|
function stackPartsToLegacyProjectConfigPartial(parts) {
|
|
@@ -2379,7 +2521,7 @@ function stackPartsToLegacyProjectConfigPartial(parts) {
|
|
|
2379
2521
|
const astroIntegration = part.settings?.astroIntegration;
|
|
2380
2522
|
if (typeof astroIntegration === "string") config.astroIntegration = astroIntegration;
|
|
2381
2523
|
} else if (part.role === "frontend" && part.ecosystem === "rust") config.rustFrontend = part.toolId;
|
|
2382
|
-
else if (part.role === "mobile") config.frontend = [...config.frontend ?? [], part.toolId];
|
|
2524
|
+
else if (part.role === "mobile" && part.ecosystem === "react-native") config.frontend = [...config.frontend ?? [], part.toolId];
|
|
2383
2525
|
else {
|
|
2384
2526
|
const legacyArrayCategory$1 = getLegacyArrayCategoryForPart(part);
|
|
2385
2527
|
if (legacyArrayCategory$1) {
|
|
@@ -2427,7 +2569,10 @@ function getLegacyCategoryForPart(part, parts) {
|
|
|
2427
2569
|
const legacyCategory = LEGACY_TYPESCRIPT_BACKEND_SINGLE_CATEGORIES[part.role] ?? LEGACY_TYPESCRIPT_BACKEND_INFRA_CATEGORIES[part.role];
|
|
2428
2570
|
if (legacyCategory) return legacyCategory;
|
|
2429
2571
|
}
|
|
2430
|
-
if (owner?.role === "mobile" && part.ecosystem === "react-native"
|
|
2572
|
+
if (owner?.role === "mobile" && part.ecosystem === "react-native") {
|
|
2573
|
+
if (part.role === "auth") return "auth";
|
|
2574
|
+
if (part.role === "payments") return "payments";
|
|
2575
|
+
}
|
|
2431
2576
|
if (owner?.role === "database" && part.ecosystem === "universal") {
|
|
2432
2577
|
const legacyCategory = LEGACY_DATABASE_SINGLE_CATEGORIES[part.role];
|
|
2433
2578
|
if (legacyCategory) return legacyCategory;
|
|
@@ -2488,7 +2633,7 @@ function stackGraphToLegacyProjectConfigForEcosystem(config, ecosystem) {
|
|
|
2488
2633
|
if (part.source === "provided" || part.ownerPartId !== frontend?.id || frontendScopedPartRoles.has(part.role)) continue;
|
|
2489
2634
|
projectLegacyCategoryFromPart(projected, part, ecosystem, parts);
|
|
2490
2635
|
}
|
|
2491
|
-
const mobileScopedPartRoles = new Set(["auth"]);
|
|
2636
|
+
const mobileScopedPartRoles = new Set(["auth", "payments"]);
|
|
2492
2637
|
for (const part of parts) {
|
|
2493
2638
|
if (part.source === "provided" || part.ownerPartId !== mobile?.id || mobileScopedPartRoles.has(part.role)) continue;
|
|
2494
2639
|
projectLegacyCategoryFromPart(projected, part, ecosystem, parts);
|
|
@@ -2506,11 +2651,43 @@ function stackGraphToLegacyProjectConfigForEcosystem(config, ecosystem) {
|
|
|
2506
2651
|
function validateStackParts(parts) {
|
|
2507
2652
|
const issues = [];
|
|
2508
2653
|
const partsById = new Map(parts.map((part) => [part.id, part]));
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2654
|
+
const seenPartIds = /* @__PURE__ */ new Set();
|
|
2655
|
+
const selectedPrimaryParts = parts.filter((part) => !part.ownerPartId && part.source !== "provided" && PRIMARY_ROLES.has(part.role));
|
|
2656
|
+
const unsupportedRepeatedPrimaryGroups = [
|
|
2657
|
+
{
|
|
2658
|
+
label: "TypeScript web frontend",
|
|
2659
|
+
parts: selectedPrimaryParts.filter((part) => part.role === "frontend" && part.ecosystem === "typescript")
|
|
2660
|
+
},
|
|
2661
|
+
{
|
|
2662
|
+
label: "TypeScript backend",
|
|
2663
|
+
parts: selectedPrimaryParts.filter((part) => part.role === "backend" && part.ecosystem === "typescript")
|
|
2664
|
+
},
|
|
2665
|
+
{
|
|
2666
|
+
label: "React Native mobile app",
|
|
2667
|
+
parts: selectedPrimaryParts.filter((part) => part.role === "mobile" && part.ecosystem === "react-native")
|
|
2668
|
+
},
|
|
2669
|
+
{
|
|
2670
|
+
label: "database",
|
|
2671
|
+
parts: selectedPrimaryParts.filter((part) => part.role === "database")
|
|
2672
|
+
}
|
|
2673
|
+
];
|
|
2674
|
+
for (const group of unsupportedRepeatedPrimaryGroups) for (const part of group.parts.slice(1)) issues.push({
|
|
2675
|
+
code: "UNSUPPORTED_REPEATED_PRIMARY",
|
|
2676
|
+
partId: part.id,
|
|
2677
|
+
role: part.role,
|
|
2678
|
+
toolId: part.toolId,
|
|
2679
|
+
message: `Only one ${group.label} is currently supported. Named repeated backend services are supported for Rust, Python, Go, Java/Kotlin, .NET, and Elixir.`
|
|
2513
2680
|
});
|
|
2681
|
+
for (const part of parts) {
|
|
2682
|
+
if (seenPartIds.has(part.id)) issues.push({
|
|
2683
|
+
code: "DUPLICATE_PART_ID",
|
|
2684
|
+
partId: part.id,
|
|
2685
|
+
role: part.role,
|
|
2686
|
+
toolId: part.toolId,
|
|
2687
|
+
message: `Stack part ID '${part.id}' is repeated. Give repeated primary services unique IDs with role:ecosystem:tool:id.`
|
|
2688
|
+
});
|
|
2689
|
+
seenPartIds.add(part.id);
|
|
2690
|
+
}
|
|
2514
2691
|
for (const part of parts) {
|
|
2515
2692
|
const definition = findDefinition(part);
|
|
2516
2693
|
if (!definition) issues.push({
|
|
@@ -2542,6 +2719,7 @@ function validateStackParts(parts) {
|
|
|
2542
2719
|
}
|
|
2543
2720
|
const byScope = /* @__PURE__ */ new Map();
|
|
2544
2721
|
for (const part of parts) {
|
|
2722
|
+
if (!part.ownerPartId && PRIMARY_ROLES.has(part.role)) continue;
|
|
2545
2723
|
const key = `${part.ownerPartId ?? "root"}:${part.role}`;
|
|
2546
2724
|
byScope.set(key, [...byScope.get(key) ?? [], part]);
|
|
2547
2725
|
}
|
|
@@ -2574,4 +2752,4 @@ function validateStackParts(parts) {
|
|
|
2574
2752
|
|
|
2575
2753
|
//#endregion
|
|
2576
2754
|
export { isBackendUtilsCompatibleBackend as C, requiresChatSdkVercelAIForExamples as E, hasTauriCompatibleFrontend as S, isExampleChatSdkAllowed as T, UI_LIBRARY_COMPATIBILITY as _, getAddonStackPartBinding as a, hasPWACompatibleFrontend as b, getStackPartOptions as c, materializeProvidedStackParts as d, parseStackPartSpecs as f, BACKEND_UTILS_COMPATIBLE_BACKENDS as g, validateStackParts as h, formatStackPartSpec as i, getStackToolDefinitions as l, stackPartsToLegacyProjectConfigPartial as m, STACK_TOOL_DEFINITIONS as n, getRoleTargetPath as o, stackGraphToLegacyProjectConfigForEcosystem as p, createStackPart as r, getStackPartCompatibilityIssueForPart as s, ELIXIR_UNSUPPORTED_GRAPH_TOOLS as t, legacyProjectConfigToStackParts as u, getUnsupportedWebDeployFrontend as v, isExampleAIAllowed as w, hasTanStackAICompatibleFrontend as x, hasDockerComposeCompatibleFrontend as y };
|
|
2577
|
-
//# sourceMappingURL=stack-graph-
|
|
2755
|
+
//# sourceMappingURL=stack-graph-CXTiPofQ.mjs.map
|