@zuplo/cli 7.5.0 → 7.5.2
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/node_modules/@posthog/types/dist/posthog-config.d.ts +16 -4
- package/node_modules/@posthog/types/dist/posthog-config.d.ts.map +1 -1
- package/node_modules/@posthog/types/package.json +1 -1
- package/node_modules/@posthog/types/src/posthog-config.ts +17 -4
- package/node_modules/@zuplo/core/customer.cli.minified.js +1 -1
- package/node_modules/@zuplo/core/index.minified.js +1 -1
- package/node_modules/@zuplo/core/package.json +1 -1
- package/node_modules/@zuplo/graphql/package.json +1 -1
- package/node_modules/@zuplo/openapi-tools/package.json +1 -1
- package/node_modules/@zuplo/otel/package.json +1 -1
- package/node_modules/@zuplo/runtime/out/esm/chunk-HEFHZ2HU.js +409 -0
- package/node_modules/@zuplo/runtime/out/esm/chunk-HEFHZ2HU.js.map +1 -0
- package/node_modules/@zuplo/runtime/out/esm/index.js +1 -1
- package/node_modules/@zuplo/runtime/out/esm/index.js.map +1 -1
- package/node_modules/@zuplo/runtime/out/esm/mcp-gateway/index.js +1 -1
- package/node_modules/@zuplo/runtime/out/esm/mcp-gateway/index.js.map +1 -1
- package/node_modules/@zuplo/runtime/out/types/index.d.ts +253 -0
- package/node_modules/@zuplo/runtime/package.json +1 -1
- package/node_modules/hono/dist/cjs/helper/ssg/ssg.js +1 -1
- package/node_modules/hono/dist/cjs/helper/ssg/utils.js +30 -10
- package/node_modules/hono/dist/cjs/middleware/cache/index.js +1 -1
- package/node_modules/hono/dist/cjs/utils/body.js +15 -3
- package/node_modules/hono/dist/cjs/utils/url.js +9 -1
- package/node_modules/hono/dist/helper/ssg/ssg.js +1 -1
- package/node_modules/hono/dist/helper/ssg/utils.js +30 -10
- package/node_modules/hono/dist/middleware/cache/index.js +1 -1
- package/node_modules/hono/dist/types/utils/url.d.ts +4 -0
- package/node_modules/hono/dist/utils/body.js +15 -3
- package/node_modules/hono/dist/utils/url.js +9 -1
- package/node_modules/hono/package.json +1 -1
- package/package.json +11 -11
- package/node_modules/@zuplo/runtime/out/esm/chunk-COF23LJ5.js +0 -403
- package/node_modules/@zuplo/runtime/out/esm/chunk-COF23LJ5.js.map +0 -1
- /package/node_modules/@zuplo/runtime/out/esm/{chunk-COF23LJ5.js.LEGAL.txt → chunk-HEFHZ2HU.js.LEGAL.txt} +0 -0
|
@@ -1065,6 +1065,154 @@ export declare interface AIGatewaySemanticCacheV2InboundPolicyOptions {
|
|
|
1065
1065
|
onUnknownShape?: OnUnknownShape_2;
|
|
1066
1066
|
}
|
|
1067
1067
|
|
|
1068
|
+
/**
|
|
1069
|
+
* Reads and writes the Smart Router result for an AI Gateway request.
|
|
1070
|
+
*
|
|
1071
|
+
* The built-in Smart Router policy `set()`s this after a successful
|
|
1072
|
+
* classification. Custom policies can `get()` it later in the same request,
|
|
1073
|
+
* or `set()` a compatible result of their own.
|
|
1074
|
+
*
|
|
1075
|
+
* @public
|
|
1076
|
+
* @example
|
|
1077
|
+
* ```typescript
|
|
1078
|
+
* import { AIGatewaySmartRouter } from "@zuplo/runtime";
|
|
1079
|
+
*
|
|
1080
|
+
* const result = AIGatewaySmartRouter.get(context);
|
|
1081
|
+
* if (result?.profile.intent === "code") {
|
|
1082
|
+
* // ...
|
|
1083
|
+
* }
|
|
1084
|
+
* ```
|
|
1085
|
+
*/
|
|
1086
|
+
export declare const AIGatewaySmartRouter: {
|
|
1087
|
+
set(context: ZuploContext, result: AIGatewaySmartRouterResult): void;
|
|
1088
|
+
get(context: ZuploContext): AIGatewaySmartRouterResult | undefined;
|
|
1089
|
+
};
|
|
1090
|
+
|
|
1091
|
+
/**
|
|
1092
|
+
* Prompt-complexity label produced by Smart Router.
|
|
1093
|
+
*
|
|
1094
|
+
* @public
|
|
1095
|
+
*/
|
|
1096
|
+
export declare type AIGatewaySmartRouterComplexity = "low" | "medium" | "high";
|
|
1097
|
+
|
|
1098
|
+
/**
|
|
1099
|
+
* Smart Router Inbound Policy for the AI Gateway
|
|
1100
|
+
* @title Smart Router
|
|
1101
|
+
* @product ai-gateway
|
|
1102
|
+
* @requiresAI
|
|
1103
|
+
*
|
|
1104
|
+
* Classifies the last user prompt by calling a dedicated classifier AI
|
|
1105
|
+
* Gateway app, stores the result on `AIGatewaySmartRouter`, and
|
|
1106
|
+
* optionally overwrites completions routing from `modelsByComplexity`.
|
|
1107
|
+
* Classification failures, timeouts, and invalid options fail open: the
|
|
1108
|
+
* original request is forwarded.
|
|
1109
|
+
*
|
|
1110
|
+
* @param request - The ZuploRequest
|
|
1111
|
+
* @param context - The ZuploContext
|
|
1112
|
+
* @param options - The policy options set in policies.json
|
|
1113
|
+
* @param policyName - The name of the policy as set in policies.json
|
|
1114
|
+
* @returns A Request or a Response
|
|
1115
|
+
*/
|
|
1116
|
+
export declare function AIGatewaySmartRouterInboundPolicy(
|
|
1117
|
+
request: ZuploRequest,
|
|
1118
|
+
context: ZuploContext,
|
|
1119
|
+
unparsedOptions: unknown,
|
|
1120
|
+
policyName: string
|
|
1121
|
+
): Promise<ZuploRequest | Response>;
|
|
1122
|
+
|
|
1123
|
+
/**
|
|
1124
|
+
* Options for the Smart Router policy: classify the last user prompt with a dedicated classifier AI Gateway app, then optionally route by complexity.
|
|
1125
|
+
* @public
|
|
1126
|
+
*/
|
|
1127
|
+
export declare interface AIGatewaySmartRouterInboundPolicyOptions {
|
|
1128
|
+
classifierAppID: ClassifierAppID;
|
|
1129
|
+
classifierAppApiKey: ClassifierAppAPIKey;
|
|
1130
|
+
classifierModel: ClassifierModel;
|
|
1131
|
+
smartRoutingEnabled?: SmartRoutingEnabled;
|
|
1132
|
+
modelsByComplexity?: ModelsByComplexity;
|
|
1133
|
+
intents?: Intents;
|
|
1134
|
+
classifierPrompt?: ClassifierPrompt;
|
|
1135
|
+
minConfidenceForRouting?: MinimumConfidenceForRouting;
|
|
1136
|
+
classifierTimeoutMs?: ClassifierTimeoutMs;
|
|
1137
|
+
maxPromptChars?: MaxPromptCharacters;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
/**
|
|
1141
|
+
* Structured classification of the last user prompt.
|
|
1142
|
+
*
|
|
1143
|
+
* @public
|
|
1144
|
+
*/
|
|
1145
|
+
export declare interface AIGatewaySmartRouterProfile {
|
|
1146
|
+
intent: string;
|
|
1147
|
+
complexity: AIGatewaySmartRouterComplexity;
|
|
1148
|
+
confidence: number;
|
|
1149
|
+
reasons: string[];
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
/**
|
|
1153
|
+
* Where the classified text was taken from on the request.
|
|
1154
|
+
*
|
|
1155
|
+
* @public
|
|
1156
|
+
*/
|
|
1157
|
+
export declare type AIGatewaySmartRouterPromptSource = "user" | "prior-user";
|
|
1158
|
+
|
|
1159
|
+
/**
|
|
1160
|
+
* Why Smart Router did or did not change the request's model.
|
|
1161
|
+
*
|
|
1162
|
+
* @public
|
|
1163
|
+
*/
|
|
1164
|
+
export declare type AIGatewaySmartRouterReason =
|
|
1165
|
+
| "applied"
|
|
1166
|
+
| "disabled"
|
|
1167
|
+
| "low-confidence"
|
|
1168
|
+
| "no-model"
|
|
1169
|
+
| "internal-error"
|
|
1170
|
+
| "unknown-intent";
|
|
1171
|
+
|
|
1172
|
+
/**
|
|
1173
|
+
* Result produced by the Smart Router policy for the current request.
|
|
1174
|
+
* Custom inbound policies can read this after Smart Router runs.
|
|
1175
|
+
*
|
|
1176
|
+
* @public
|
|
1177
|
+
*/
|
|
1178
|
+
export declare interface AIGatewaySmartRouterResult {
|
|
1179
|
+
profile: AIGatewaySmartRouterProfile;
|
|
1180
|
+
usage: AIGatewaySmartRouterTokenUsage;
|
|
1181
|
+
classifierModel: string;
|
|
1182
|
+
routing: AIGatewaySmartRouterRoutingDecision;
|
|
1183
|
+
durationMs: number;
|
|
1184
|
+
promptSource: AIGatewaySmartRouterPromptSource;
|
|
1185
|
+
promptLength: number;
|
|
1186
|
+
promptTruncated: boolean;
|
|
1187
|
+
unknownIntent: boolean;
|
|
1188
|
+
smartRouting: {
|
|
1189
|
+
enabled: boolean;
|
|
1190
|
+
applied: boolean;
|
|
1191
|
+
reason: AIGatewaySmartRouterReason;
|
|
1192
|
+
minConfidence: number;
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
/**
|
|
1197
|
+
* Model chosen from `modelsByComplexity` for the classified complexity, if any.
|
|
1198
|
+
*
|
|
1199
|
+
* @public
|
|
1200
|
+
*/
|
|
1201
|
+
export declare interface AIGatewaySmartRouterRoutingDecision {
|
|
1202
|
+
model?: string;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
/**
|
|
1206
|
+
* Classifier token usage reported by the classifier chat/completions call.
|
|
1207
|
+
*
|
|
1208
|
+
* @public
|
|
1209
|
+
*/
|
|
1210
|
+
export declare interface AIGatewaySmartRouterTokenUsage {
|
|
1211
|
+
promptTokens: number;
|
|
1212
|
+
completionTokens: number;
|
|
1213
|
+
totalTokens: number;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1068
1216
|
/**
|
|
1069
1217
|
* The options for this policy.
|
|
1070
1218
|
* @public
|
|
@@ -3589,6 +3737,36 @@ declare type Claim = string;
|
|
|
3589
3737
|
*/
|
|
3590
3738
|
declare type ClaimCheck = EqualsRule | InRule | StartsWithRule;
|
|
3591
3739
|
|
|
3740
|
+
/**
|
|
3741
|
+
* API key sent as `Authorization: Bearer` when invoking the classifier app.
|
|
3742
|
+
* @public
|
|
3743
|
+
*/
|
|
3744
|
+
declare type ClassifierAppAPIKey = string;
|
|
3745
|
+
|
|
3746
|
+
/**
|
|
3747
|
+
* The AI Gateway application id whose `/v1/chat/completions` route runs the classifier. The policy calls `/{classifierAppID}/v1/chat/completions` via `context.invokeRoute`.
|
|
3748
|
+
* @public
|
|
3749
|
+
*/
|
|
3750
|
+
declare type ClassifierAppID = string;
|
|
3751
|
+
|
|
3752
|
+
/**
|
|
3753
|
+
* The `providerName/model` reference the classifier chat/completions request should use.
|
|
3754
|
+
* @public
|
|
3755
|
+
*/
|
|
3756
|
+
declare type ClassifierModel = string;
|
|
3757
|
+
|
|
3758
|
+
/**
|
|
3759
|
+
* System prompt for the classifier. If it includes `{{intents}}`, that placeholder is replaced with the configured intent id/description list. A string or an array of lines (joined with newlines). Omit to use the built-in classifier prompt.
|
|
3760
|
+
* @public
|
|
3761
|
+
*/
|
|
3762
|
+
declare type ClassifierPrompt = string | [string, ...string[]];
|
|
3763
|
+
|
|
3764
|
+
/**
|
|
3765
|
+
* How long to wait for the classifier `invokeRoute` call before skipping classification and forwarding the original request.
|
|
3766
|
+
* @public
|
|
3767
|
+
*/
|
|
3768
|
+
declare type ClassifierTimeoutMs = number;
|
|
3769
|
+
|
|
3592
3770
|
/**
|
|
3593
3771
|
* Removes all headers from the incoming request except for those in the exclude list.
|
|
3594
3772
|
*
|
|
@@ -5778,6 +5956,12 @@ declare interface HeaderCredentialsConfig {
|
|
|
5778
5956
|
headerName: string;
|
|
5779
5957
|
}
|
|
5780
5958
|
|
|
5959
|
+
/**
|
|
5960
|
+
* Model used for prompts classified as high complexity.
|
|
5961
|
+
* @public
|
|
5962
|
+
*/
|
|
5963
|
+
declare type HighComplexityModel = string;
|
|
5964
|
+
|
|
5781
5965
|
/**
|
|
5782
5966
|
* Sets HTTP deprecation headers on the outgoing response following the IETF
|
|
5783
5967
|
* HTTP Deprecation Header standard. Supports the Deprecation, Sunset, and Link
|
|
@@ -7654,6 +7838,35 @@ declare interface InRule {
|
|
|
7654
7838
|
in: [string | number | boolean, ...(string | number | boolean)[]];
|
|
7655
7839
|
}
|
|
7656
7840
|
|
|
7841
|
+
/**
|
|
7842
|
+
* Short description shown to the classifier for this intent.
|
|
7843
|
+
* @public
|
|
7844
|
+
*/
|
|
7845
|
+
declare type IntentDescription = string;
|
|
7846
|
+
|
|
7847
|
+
/**
|
|
7848
|
+
* Stable intent identifier written into the classifier JSON schema enum.
|
|
7849
|
+
* @public
|
|
7850
|
+
*/
|
|
7851
|
+
declare type IntentID = string;
|
|
7852
|
+
|
|
7853
|
+
/**
|
|
7854
|
+
* Labels the classifier may assign. Used to build the JSON schema enum and injected into `classifierPrompt` at `{{intents}}`. Omit to use the built-in taxonomy (code, summarization, translation, qa, conversation, classification, creative_writing, agentic, document_qa, other).
|
|
7855
|
+
*
|
|
7856
|
+
* @minItems 1
|
|
7857
|
+
* @public
|
|
7858
|
+
*/
|
|
7859
|
+
declare type Intents = [
|
|
7860
|
+
{
|
|
7861
|
+
id: IntentID;
|
|
7862
|
+
description: IntentDescription;
|
|
7863
|
+
},
|
|
7864
|
+
...{
|
|
7865
|
+
id: IntentID;
|
|
7866
|
+
description: IntentDescription;
|
|
7867
|
+
}[],
|
|
7868
|
+
];
|
|
7869
|
+
|
|
7657
7870
|
/**
|
|
7658
7871
|
* Restricts which client IP addresses can call a route using an allow list, a
|
|
7659
7872
|
* deny list, or both. Entries are individual IP addresses or CIDR ranges, in
|
|
@@ -8298,12 +8511,24 @@ declare type LokiTransportVersion = 1 | 2;
|
|
|
8298
8511
|
|
|
8299
8512
|
/* Excluded from this release type: LookupResult */
|
|
8300
8513
|
|
|
8514
|
+
/**
|
|
8515
|
+
* Model used for prompts classified as low complexity.
|
|
8516
|
+
* @public
|
|
8517
|
+
*/
|
|
8518
|
+
declare type LowComplexityModel = string;
|
|
8519
|
+
|
|
8301
8520
|
/**
|
|
8302
8521
|
* Skip caching entirely when the conversation has more than this many messages. Unset by default (no limit). Set a bound to guard against topic drift: the cache key covers only the last recentMessageCount messages, so the deeper a conversation goes, the more likely a match on recent messages alone ignores earlier context that should change the answer.
|
|
8303
8522
|
* @public
|
|
8304
8523
|
*/
|
|
8305
8524
|
declare type MaxConversationLength = number;
|
|
8306
8525
|
|
|
8526
|
+
/**
|
|
8527
|
+
* Maximum characters of user prompt sent to the classifier. Longer prompts are truncated.
|
|
8528
|
+
* @public
|
|
8529
|
+
*/
|
|
8530
|
+
declare type MaxPromptCharacters = number;
|
|
8531
|
+
|
|
8307
8532
|
/**
|
|
8308
8533
|
* Authenticate MCP gateway requests using a gateway-issued OAuth access token,
|
|
8309
8534
|
* with browser login delegated to Auth0.
|
|
@@ -10087,6 +10312,12 @@ export declare interface McpWorkosOAuthInboundPolicyOptions {
|
|
|
10087
10312
|
};
|
|
10088
10313
|
}
|
|
10089
10314
|
|
|
10315
|
+
/**
|
|
10316
|
+
* Model used for prompts classified as medium complexity.
|
|
10317
|
+
* @public
|
|
10318
|
+
*/
|
|
10319
|
+
declare type MediumComplexityModel = string;
|
|
10320
|
+
|
|
10090
10321
|
declare interface MemoryCacheOptions {
|
|
10091
10322
|
maxSize: number;
|
|
10092
10323
|
}
|
|
@@ -10270,6 +10501,12 @@ declare interface MetricsType {
|
|
|
10270
10501
|
responseContentLength?: boolean;
|
|
10271
10502
|
}
|
|
10272
10503
|
|
|
10504
|
+
/**
|
|
10505
|
+
* Minimum confidence (0–1) required before applying model routing. Unknown intents are capped strictly below this threshold.
|
|
10506
|
+
* @public
|
|
10507
|
+
*/
|
|
10508
|
+
declare type MinimumConfidenceForRouting = number;
|
|
10509
|
+
|
|
10273
10510
|
/**
|
|
10274
10511
|
* The options for this policy.
|
|
10275
10512
|
* @public
|
|
@@ -10322,6 +10559,16 @@ declare interface Models {
|
|
|
10322
10559
|
embeddings?: EmbeddingsModels;
|
|
10323
10560
|
}
|
|
10324
10561
|
|
|
10562
|
+
/**
|
|
10563
|
+
* Optional `providerName/model` routing by classified complexity. Used only when `smartRoutingEnabled` is true.
|
|
10564
|
+
* @public
|
|
10565
|
+
*/
|
|
10566
|
+
declare interface ModelsByComplexity {
|
|
10567
|
+
low?: LowComplexityModel;
|
|
10568
|
+
medium?: MediumComplexityModel;
|
|
10569
|
+
high?: HighComplexityModel;
|
|
10570
|
+
}
|
|
10571
|
+
|
|
10325
10572
|
declare type Modify<T, R> = Omit<T, keyof R> & R;
|
|
10326
10573
|
|
|
10327
10574
|
declare interface MoesifContext {
|
|
@@ -13907,6 +14154,12 @@ export declare interface SleepInboundPolicyOptions {
|
|
|
13907
14154
|
sleepInMs: number;
|
|
13908
14155
|
}
|
|
13909
14156
|
|
|
14157
|
+
/**
|
|
14158
|
+
* When true, apply model routing from `modelsByComplexity` when confidence is high enough and the intent is known. Classification still runs when false.
|
|
14159
|
+
* @public
|
|
14160
|
+
*/
|
|
14161
|
+
declare type SmartRoutingEnabled = boolean;
|
|
14162
|
+
|
|
13910
14163
|
declare interface SplunkLoggingOptions {
|
|
13911
14164
|
/**
|
|
13912
14165
|
* The URL for the Splunk HTTP Event Collector (HEC) endpoint.
|
|
@@ -206,7 +206,7 @@ const saveContentToFile = async (data, fsModule, outDir, extensionMap) => {
|
|
|
206
206
|
const { routePath, content, mimeType } = awaitedData;
|
|
207
207
|
const filePath = generateFilePath(routePath, outDir, mimeType, extensionMap);
|
|
208
208
|
const dirPath = (0, import_utils2.dirname)(filePath);
|
|
209
|
-
if (!createdDirs.has(dirPath)) {
|
|
209
|
+
if (dirPath !== "" && !createdDirs.has(dirPath)) {
|
|
210
210
|
await fsModule.mkdir(dirPath, { recursive: true });
|
|
211
211
|
createdDirs.add(dirPath);
|
|
212
212
|
}
|
|
@@ -33,8 +33,14 @@ const dirname = (path) => {
|
|
|
33
33
|
const normalizePath = (path) => {
|
|
34
34
|
return path.replace(/(\\)/g, "/").replace(/\/$/g, "");
|
|
35
35
|
};
|
|
36
|
-
const
|
|
37
|
-
|
|
36
|
+
const getUncRoot = (path) => {
|
|
37
|
+
const uncRoot = path.replace(/\\/g, "/").match(/^\/\/([^/]+)\/([^/]+)/);
|
|
38
|
+
if (uncRoot) {
|
|
39
|
+
return `${uncRoot[1].toLowerCase()}/${uncRoot[2].toLowerCase()}`;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const handleParent = (resultPaths) => {
|
|
43
|
+
if (resultPaths.length === 0 || resultPaths[resultPaths.length - 1] === "..") {
|
|
38
44
|
resultPaths.push("..");
|
|
39
45
|
} else {
|
|
40
46
|
resultPaths.pop();
|
|
@@ -47,22 +53,20 @@ const handleNonDot = (path, resultPaths) => {
|
|
|
47
53
|
}
|
|
48
54
|
};
|
|
49
55
|
const handleSegments = (paths, resultPaths) => {
|
|
50
|
-
let beforeParentFlag = false;
|
|
51
56
|
for (const path of paths) {
|
|
52
57
|
if (path === "..") {
|
|
53
|
-
handleParent(resultPaths
|
|
54
|
-
beforeParentFlag = true;
|
|
58
|
+
handleParent(resultPaths);
|
|
55
59
|
} else {
|
|
56
60
|
handleNonDot(path, resultPaths);
|
|
57
|
-
beforeParentFlag = false;
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
63
|
};
|
|
61
64
|
const joinPaths = (...paths) => {
|
|
65
|
+
const hasUncPrefix = getUncRoot(paths[0]) !== void 0;
|
|
62
66
|
paths = paths.map(normalizePath);
|
|
63
67
|
const resultPaths = [];
|
|
64
68
|
handleSegments(paths.join("/").split("/"), resultPaths);
|
|
65
|
-
return (paths[0][0] === "/" ? "/" : "") + resultPaths.join("/");
|
|
69
|
+
return (hasUncPrefix ? "//" : paths[0][0] === "/" ? "/" : "") + resultPaths.join("/");
|
|
66
70
|
};
|
|
67
71
|
const filterStaticGenerateRoutes = (hono) => {
|
|
68
72
|
return hono.routes.reduce((acc, { method, handler, path }) => {
|
|
@@ -76,10 +80,26 @@ const filterStaticGenerateRoutes = (hono) => {
|
|
|
76
80
|
const isDynamicRoute = (path) => {
|
|
77
81
|
return path.split("/").some((segment) => segment.startsWith(":") || segment.includes("*"));
|
|
78
82
|
};
|
|
83
|
+
const toSegments = (path) => path === "" ? [] : path.split("/");
|
|
84
|
+
const getPathRoot = (path) => {
|
|
85
|
+
const normalizedPath = path.replace(/\\/g, "/");
|
|
86
|
+
const uncRoot = getUncRoot(normalizedPath);
|
|
87
|
+
if (uncRoot) {
|
|
88
|
+
return `unc:${uncRoot}`;
|
|
89
|
+
}
|
|
90
|
+
const driveRoot = normalizedPath.match(/^([A-Za-z]):/);
|
|
91
|
+
if (driveRoot) {
|
|
92
|
+
const kind = normalizedPath[2] === "/" ? "drive-absolute" : "drive-relative";
|
|
93
|
+
return `${kind}:${driveRoot[1].toLowerCase()}`;
|
|
94
|
+
}
|
|
95
|
+
return normalizedPath.startsWith("/") ? "absolute" : "relative";
|
|
96
|
+
};
|
|
79
97
|
const ensureWithinOutDir = (outDir, filePath) => {
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
|
|
98
|
+
const outDirSegments = toSegments(joinPaths(outDir));
|
|
99
|
+
const filePathSegments = toSegments(joinPaths(filePath));
|
|
100
|
+
const hasMismatchedPathRoot = getPathRoot(outDir) !== getPathRoot(filePath);
|
|
101
|
+
const climbsAboveOutDir = filePathSegments[outDirSegments.length] === "..";
|
|
102
|
+
if (hasMismatchedPathRoot || filePathSegments.length <= outDirSegments.length || !outDirSegments.every((segment, i) => segment === filePathSegments[i]) || climbsAboveOutDir) {
|
|
83
103
|
throw new Error(`Path traversal detected: "${filePath}" is outside the output directory`);
|
|
84
104
|
}
|
|
85
105
|
};
|
|
@@ -44,7 +44,7 @@ const parseVaryDirectives = (vary) => {
|
|
|
44
44
|
};
|
|
45
45
|
const createCacheKey = (key, requestUrl, request, varyHeaders) => {
|
|
46
46
|
const url = new URL(cacheKeyPath, requestUrl);
|
|
47
|
-
url.searchParams.append(cacheKeyParameter, key
|
|
47
|
+
url.searchParams.append(cacheKeyParameter, key);
|
|
48
48
|
url.searchParams.append(cacheMethodKeyParameter, request.method);
|
|
49
49
|
if (request.method === "QUERY") {
|
|
50
50
|
url.searchParams.append(queryDigestKeyParameter, request.digest);
|
|
@@ -21,6 +21,8 @@ __export(body_exports, {
|
|
|
21
21
|
});
|
|
22
22
|
module.exports = __toCommonJS(body_exports);
|
|
23
23
|
var import_buffer = require("./buffer");
|
|
24
|
+
const MAX_NESTING_DEPTH = 32;
|
|
25
|
+
const MAX_NESTED_OBJECTS = 1e4;
|
|
24
26
|
const isRawRequest = (request) => "headers" in request;
|
|
25
27
|
const parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => {
|
|
26
28
|
const { all = false, dot = false } = options;
|
|
@@ -53,6 +55,7 @@ async function parseFormData(request, options) {
|
|
|
53
55
|
}
|
|
54
56
|
function convertFormDataToBodyData(formData, options) {
|
|
55
57
|
const form = /* @__PURE__ */ Object.create(null);
|
|
58
|
+
const nestingState = { count: 0 };
|
|
56
59
|
formData.forEach((value, key) => {
|
|
57
60
|
const shouldParseAllValues = options.all || key.endsWith("[]");
|
|
58
61
|
if (!shouldParseAllValues) {
|
|
@@ -65,7 +68,7 @@ function convertFormDataToBodyData(formData, options) {
|
|
|
65
68
|
Object.entries(form).forEach(([key, value]) => {
|
|
66
69
|
const shouldParseDotValues = key.includes(".");
|
|
67
70
|
if (shouldParseDotValues) {
|
|
68
|
-
handleParsingNestedValues(form, key, value);
|
|
71
|
+
handleParsingNestedValues(form, key, value, nestingState);
|
|
69
72
|
delete form[key];
|
|
70
73
|
}
|
|
71
74
|
});
|
|
@@ -88,23 +91,32 @@ const handleParsingAllValues = (form, key, value) => {
|
|
|
88
91
|
}
|
|
89
92
|
}
|
|
90
93
|
};
|
|
91
|
-
const handleParsingNestedValues = (form, key, value) => {
|
|
94
|
+
const handleParsingNestedValues = (form, key, value, state) => {
|
|
92
95
|
if (/(?:^|\.)__proto__\./.test(key)) {
|
|
93
96
|
return;
|
|
94
97
|
}
|
|
95
98
|
let nestedForm = form;
|
|
96
|
-
const keys = key.split(".");
|
|
99
|
+
const keys = key.split(".", MAX_NESTING_DEPTH + 2);
|
|
100
|
+
if (keys.length > MAX_NESTING_DEPTH + 1) {
|
|
101
|
+
throwNestingLimitExceeded();
|
|
102
|
+
}
|
|
97
103
|
keys.forEach((key2, index) => {
|
|
98
104
|
if (index === keys.length - 1) {
|
|
99
105
|
nestedForm[key2] = value;
|
|
100
106
|
} else {
|
|
101
107
|
if (!nestedForm[key2] || typeof nestedForm[key2] !== "object" || Array.isArray(nestedForm[key2]) || nestedForm[key2] instanceof File) {
|
|
108
|
+
if (state.count++ >= MAX_NESTED_OBJECTS) {
|
|
109
|
+
throwNestingLimitExceeded();
|
|
110
|
+
}
|
|
102
111
|
nestedForm[key2] = /* @__PURE__ */ Object.create(null);
|
|
103
112
|
}
|
|
104
113
|
nestedForm = nestedForm[key2];
|
|
105
114
|
}
|
|
106
115
|
});
|
|
107
116
|
};
|
|
117
|
+
const throwNestingLimitExceeded = () => {
|
|
118
|
+
throw new Error("Nesting limit exceeded");
|
|
119
|
+
};
|
|
108
120
|
// Annotate the CommonJS export names for ESM import in node:
|
|
109
121
|
0 && (module.exports = {
|
|
110
122
|
parseBody
|
|
@@ -119,7 +119,11 @@ const getPath = (request) => {
|
|
|
119
119
|
};
|
|
120
120
|
const getQueryStrings = (url) => {
|
|
121
121
|
const queryIndex = url.indexOf("?", 8);
|
|
122
|
-
|
|
122
|
+
if (queryIndex === -1) {
|
|
123
|
+
return "";
|
|
124
|
+
}
|
|
125
|
+
const hashIndex = url.indexOf("#", 8);
|
|
126
|
+
return hashIndex === -1 ? url.slice(queryIndex) : queryIndex < hashIndex ? url.slice(queryIndex, hashIndex) : "";
|
|
123
127
|
};
|
|
124
128
|
const getPathNoStrict = (request) => {
|
|
125
129
|
const result = getPath(request);
|
|
@@ -166,6 +170,10 @@ const _decodeURI = (value) => {
|
|
|
166
170
|
return tryDecodeURIComponent(value);
|
|
167
171
|
};
|
|
168
172
|
const _getQueryParam = (url, key, multiple) => {
|
|
173
|
+
const hashIndex = url.indexOf("#", 8);
|
|
174
|
+
if (hashIndex !== -1) {
|
|
175
|
+
url = url.slice(0, hashIndex);
|
|
176
|
+
}
|
|
169
177
|
let encoded;
|
|
170
178
|
if (!multiple && key && key.indexOf("%") === -1 && key.indexOf("+") === -1) {
|
|
171
179
|
let keyIndex2 = url.indexOf("?", 8);
|
|
@@ -184,7 +184,7 @@ var saveContentToFile = async (data, fsModule, outDir, extensionMap) => {
|
|
|
184
184
|
const { routePath, content, mimeType } = awaitedData;
|
|
185
185
|
const filePath = generateFilePath(routePath, outDir, mimeType, extensionMap);
|
|
186
186
|
const dirPath = dirname(filePath);
|
|
187
|
-
if (!createdDirs.has(dirPath)) {
|
|
187
|
+
if (dirPath !== "" && !createdDirs.has(dirPath)) {
|
|
188
188
|
await fsModule.mkdir(dirPath, { recursive: true });
|
|
189
189
|
createdDirs.add(dirPath);
|
|
190
190
|
}
|
|
@@ -8,8 +8,14 @@ var dirname = (path) => {
|
|
|
8
8
|
var normalizePath = (path) => {
|
|
9
9
|
return path.replace(/(\\)/g, "/").replace(/\/$/g, "");
|
|
10
10
|
};
|
|
11
|
-
var
|
|
12
|
-
|
|
11
|
+
var getUncRoot = (path) => {
|
|
12
|
+
const uncRoot = path.replace(/\\/g, "/").match(/^\/\/([^/]+)\/([^/]+)/);
|
|
13
|
+
if (uncRoot) {
|
|
14
|
+
return `${uncRoot[1].toLowerCase()}/${uncRoot[2].toLowerCase()}`;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var handleParent = (resultPaths) => {
|
|
18
|
+
if (resultPaths.length === 0 || resultPaths[resultPaths.length - 1] === "..") {
|
|
13
19
|
resultPaths.push("..");
|
|
14
20
|
} else {
|
|
15
21
|
resultPaths.pop();
|
|
@@ -22,22 +28,20 @@ var handleNonDot = (path, resultPaths) => {
|
|
|
22
28
|
}
|
|
23
29
|
};
|
|
24
30
|
var handleSegments = (paths, resultPaths) => {
|
|
25
|
-
let beforeParentFlag = false;
|
|
26
31
|
for (const path of paths) {
|
|
27
32
|
if (path === "..") {
|
|
28
|
-
handleParent(resultPaths
|
|
29
|
-
beforeParentFlag = true;
|
|
33
|
+
handleParent(resultPaths);
|
|
30
34
|
} else {
|
|
31
35
|
handleNonDot(path, resultPaths);
|
|
32
|
-
beforeParentFlag = false;
|
|
33
36
|
}
|
|
34
37
|
}
|
|
35
38
|
};
|
|
36
39
|
var joinPaths = (...paths) => {
|
|
40
|
+
const hasUncPrefix = getUncRoot(paths[0]) !== void 0;
|
|
37
41
|
paths = paths.map(normalizePath);
|
|
38
42
|
const resultPaths = [];
|
|
39
43
|
handleSegments(paths.join("/").split("/"), resultPaths);
|
|
40
|
-
return (paths[0][0] === "/" ? "/" : "") + resultPaths.join("/");
|
|
44
|
+
return (hasUncPrefix ? "//" : paths[0][0] === "/" ? "/" : "") + resultPaths.join("/");
|
|
41
45
|
};
|
|
42
46
|
var filterStaticGenerateRoutes = (hono) => {
|
|
43
47
|
return hono.routes.reduce((acc, { method, handler, path }) => {
|
|
@@ -51,10 +55,26 @@ var filterStaticGenerateRoutes = (hono) => {
|
|
|
51
55
|
var isDynamicRoute = (path) => {
|
|
52
56
|
return path.split("/").some((segment) => segment.startsWith(":") || segment.includes("*"));
|
|
53
57
|
};
|
|
58
|
+
var toSegments = (path) => path === "" ? [] : path.split("/");
|
|
59
|
+
var getPathRoot = (path) => {
|
|
60
|
+
const normalizedPath = path.replace(/\\/g, "/");
|
|
61
|
+
const uncRoot = getUncRoot(normalizedPath);
|
|
62
|
+
if (uncRoot) {
|
|
63
|
+
return `unc:${uncRoot}`;
|
|
64
|
+
}
|
|
65
|
+
const driveRoot = normalizedPath.match(/^([A-Za-z]):/);
|
|
66
|
+
if (driveRoot) {
|
|
67
|
+
const kind = normalizedPath[2] === "/" ? "drive-absolute" : "drive-relative";
|
|
68
|
+
return `${kind}:${driveRoot[1].toLowerCase()}`;
|
|
69
|
+
}
|
|
70
|
+
return normalizedPath.startsWith("/") ? "absolute" : "relative";
|
|
71
|
+
};
|
|
54
72
|
var ensureWithinOutDir = (outDir, filePath) => {
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
|
|
73
|
+
const outDirSegments = toSegments(joinPaths(outDir));
|
|
74
|
+
const filePathSegments = toSegments(joinPaths(filePath));
|
|
75
|
+
const hasMismatchedPathRoot = getPathRoot(outDir) !== getPathRoot(filePath);
|
|
76
|
+
const climbsAboveOutDir = filePathSegments[outDirSegments.length] === "..";
|
|
77
|
+
if (hasMismatchedPathRoot || filePathSegments.length <= outDirSegments.length || !outDirSegments.every((segment, i) => segment === filePathSegments[i]) || climbsAboveOutDir) {
|
|
58
78
|
throw new Error(`Path traversal detected: "${filePath}" is outside the output directory`);
|
|
59
79
|
}
|
|
60
80
|
};
|
|
@@ -23,7 +23,7 @@ var parseVaryDirectives = (vary) => {
|
|
|
23
23
|
};
|
|
24
24
|
var createCacheKey = (key, requestUrl, request, varyHeaders) => {
|
|
25
25
|
const url = new URL(cacheKeyPath, requestUrl);
|
|
26
|
-
url.searchParams.append(cacheKeyParameter, key
|
|
26
|
+
url.searchParams.append(cacheKeyParameter, key);
|
|
27
27
|
url.searchParams.append(cacheMethodKeyParameter, request.method);
|
|
28
28
|
if (request.method === "QUERY") {
|
|
29
29
|
url.searchParams.append(queryDigestKeyParameter, request.digest);
|
|
@@ -19,6 +19,10 @@ export declare const tryDecode: (str: string, decoder: Decoder) => string;
|
|
|
19
19
|
*/
|
|
20
20
|
export declare const tryDecodeURI: (str: string) => string;
|
|
21
21
|
export declare const getPath: (request: Request) => string;
|
|
22
|
+
/**
|
|
23
|
+
* @deprecated
|
|
24
|
+
* Use the `URL` API instead.
|
|
25
|
+
*/
|
|
22
26
|
export declare const getQueryStrings: (url: string) => string;
|
|
23
27
|
export declare const getPathNoStrict: (request: Request) => string;
|
|
24
28
|
/**
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// src/utils/body.ts
|
|
2
2
|
import { bufferToFormData } from "./buffer.js";
|
|
3
|
+
var MAX_NESTING_DEPTH = 32;
|
|
4
|
+
var MAX_NESTED_OBJECTS = 1e4;
|
|
3
5
|
var isRawRequest = (request) => "headers" in request;
|
|
4
6
|
var parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => {
|
|
5
7
|
const { all = false, dot = false } = options;
|
|
@@ -32,6 +34,7 @@ async function parseFormData(request, options) {
|
|
|
32
34
|
}
|
|
33
35
|
function convertFormDataToBodyData(formData, options) {
|
|
34
36
|
const form = /* @__PURE__ */ Object.create(null);
|
|
37
|
+
const nestingState = { count: 0 };
|
|
35
38
|
formData.forEach((value, key) => {
|
|
36
39
|
const shouldParseAllValues = options.all || key.endsWith("[]");
|
|
37
40
|
if (!shouldParseAllValues) {
|
|
@@ -44,7 +47,7 @@ function convertFormDataToBodyData(formData, options) {
|
|
|
44
47
|
Object.entries(form).forEach(([key, value]) => {
|
|
45
48
|
const shouldParseDotValues = key.includes(".");
|
|
46
49
|
if (shouldParseDotValues) {
|
|
47
|
-
handleParsingNestedValues(form, key, value);
|
|
50
|
+
handleParsingNestedValues(form, key, value, nestingState);
|
|
48
51
|
delete form[key];
|
|
49
52
|
}
|
|
50
53
|
});
|
|
@@ -67,23 +70,32 @@ var handleParsingAllValues = (form, key, value) => {
|
|
|
67
70
|
}
|
|
68
71
|
}
|
|
69
72
|
};
|
|
70
|
-
var handleParsingNestedValues = (form, key, value) => {
|
|
73
|
+
var handleParsingNestedValues = (form, key, value, state) => {
|
|
71
74
|
if (/(?:^|\.)__proto__\./.test(key)) {
|
|
72
75
|
return;
|
|
73
76
|
}
|
|
74
77
|
let nestedForm = form;
|
|
75
|
-
const keys = key.split(".");
|
|
78
|
+
const keys = key.split(".", MAX_NESTING_DEPTH + 2);
|
|
79
|
+
if (keys.length > MAX_NESTING_DEPTH + 1) {
|
|
80
|
+
throwNestingLimitExceeded();
|
|
81
|
+
}
|
|
76
82
|
keys.forEach((key2, index) => {
|
|
77
83
|
if (index === keys.length - 1) {
|
|
78
84
|
nestedForm[key2] = value;
|
|
79
85
|
} else {
|
|
80
86
|
if (!nestedForm[key2] || typeof nestedForm[key2] !== "object" || Array.isArray(nestedForm[key2]) || nestedForm[key2] instanceof File) {
|
|
87
|
+
if (state.count++ >= MAX_NESTED_OBJECTS) {
|
|
88
|
+
throwNestingLimitExceeded();
|
|
89
|
+
}
|
|
81
90
|
nestedForm[key2] = /* @__PURE__ */ Object.create(null);
|
|
82
91
|
}
|
|
83
92
|
nestedForm = nestedForm[key2];
|
|
84
93
|
}
|
|
85
94
|
});
|
|
86
95
|
};
|
|
96
|
+
var throwNestingLimitExceeded = () => {
|
|
97
|
+
throw new Error("Nesting limit exceeded");
|
|
98
|
+
};
|
|
87
99
|
export {
|
|
88
100
|
parseBody
|
|
89
101
|
};
|