vovk 3.0.0-draft.33 → 3.0.0-draft.331

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.
Files changed (196) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +8 -96
  3. package/bin/index.mjs +8 -0
  4. package/{HttpException.d.ts → cjs/HttpException.d.ts} +2 -2
  5. package/{HttpException.js → cjs/HttpException.js} +3 -3
  6. package/cjs/JSONLinesResponse.d.ts +19 -0
  7. package/cjs/JSONLinesResponse.js +93 -0
  8. package/{VovkApp.d.ts → cjs/VovkApp.d.ts} +10 -9
  9. package/cjs/VovkApp.js +202 -0
  10. package/cjs/client/createRPC.d.ts +3 -0
  11. package/cjs/client/createRPC.js +90 -0
  12. package/cjs/client/defaultHandler.d.ts +6 -0
  13. package/cjs/client/defaultHandler.js +29 -0
  14. package/cjs/client/defaultStreamHandler.d.ts +9 -0
  15. package/{client → cjs/client}/defaultStreamHandler.js +25 -13
  16. package/cjs/client/fetcher.d.ts +14 -0
  17. package/cjs/client/fetcher.js +93 -0
  18. package/cjs/client/index.d.ts +4 -0
  19. package/cjs/client/index.js +10 -0
  20. package/cjs/client/progressive.d.ts +9 -0
  21. package/cjs/client/progressive.js +45 -0
  22. package/cjs/client/types.d.ts +123 -0
  23. package/{createVovkApp.d.ts → cjs/createVovkApp.d.ts} +12 -11
  24. package/cjs/createVovkApp.js +133 -0
  25. package/cjs/index.d.ts +65 -0
  26. package/cjs/index.js +38 -0
  27. package/cjs/openapi/error.d.ts +2 -0
  28. package/cjs/openapi/error.js +100 -0
  29. package/cjs/openapi/generateFnName.d.ts +23 -0
  30. package/cjs/openapi/generateFnName.js +81 -0
  31. package/cjs/openapi/index.d.ts +12 -0
  32. package/cjs/openapi/index.js +21 -0
  33. package/cjs/openapi/openAPIToVovkSchema/applyComponentsSchemas.d.ts +3 -0
  34. package/cjs/openapi/openAPIToVovkSchema/applyComponentsSchemas.js +67 -0
  35. package/cjs/openapi/openAPIToVovkSchema/index.d.ts +4 -0
  36. package/cjs/openapi/openAPIToVovkSchema/index.js +192 -0
  37. package/cjs/openapi/openAPIToVovkSchema/inlineRefs.d.ts +10 -0
  38. package/cjs/openapi/openAPIToVovkSchema/inlineRefs.js +102 -0
  39. package/cjs/openapi/vovkSchemaToOpenAPI.d.ts +9 -0
  40. package/cjs/openapi/vovkSchemaToOpenAPI.js +233 -0
  41. package/cjs/types.d.ts +400 -0
  42. package/cjs/types.js +74 -0
  43. package/cjs/utils/camelCase.d.ts +6 -0
  44. package/cjs/utils/camelCase.js +37 -0
  45. package/cjs/utils/createCodeExamples.d.ts +19 -0
  46. package/cjs/utils/createCodeExamples.js +110 -0
  47. package/cjs/utils/createDecorator.d.ts +6 -0
  48. package/{createDecorator.js → cjs/utils/createDecorator.js} +26 -16
  49. package/cjs/utils/createLLMTools.d.ts +44 -0
  50. package/cjs/utils/createLLMTools.js +118 -0
  51. package/cjs/utils/createStandardValidation.d.ts +81 -0
  52. package/cjs/utils/createStandardValidation.js +33 -0
  53. package/cjs/utils/generateStaticAPI.d.ts +4 -0
  54. package/cjs/utils/generateStaticAPI.js +30 -0
  55. package/cjs/utils/getJSONSchemaExample.d.ts +8 -0
  56. package/cjs/utils/getJSONSchemaExample.js +234 -0
  57. package/cjs/utils/getJSONSchemaSample.d.ts +2 -0
  58. package/cjs/utils/getJSONSchemaSample.js +167 -0
  59. package/cjs/utils/getSchema.d.ts +21 -0
  60. package/cjs/utils/getSchema.js +38 -0
  61. package/cjs/utils/multitenant.d.ts +24 -0
  62. package/cjs/utils/multitenant.js +170 -0
  63. package/cjs/utils/parseQuery.d.ts +25 -0
  64. package/cjs/utils/parseQuery.js +156 -0
  65. package/{utils → cjs/utils}/reqForm.d.ts +1 -2
  66. package/cjs/utils/reqForm.js +33 -0
  67. package/{utils → cjs/utils}/reqMeta.d.ts +1 -2
  68. package/cjs/utils/reqQuery.d.ts +2 -0
  69. package/cjs/utils/reqQuery.js +10 -0
  70. package/cjs/utils/serializeQuery.d.ts +13 -0
  71. package/cjs/utils/serializeQuery.js +65 -0
  72. package/cjs/utils/setHandlerSchema.d.ts +4 -0
  73. package/cjs/utils/setHandlerSchema.js +15 -0
  74. package/cjs/utils/upperFirst.d.ts +1 -0
  75. package/cjs/utils/upperFirst.js +6 -0
  76. package/cjs/utils/withValidationLibrary.d.ts +76 -0
  77. package/cjs/utils/withValidationLibrary.js +123 -0
  78. package/mjs/HttpException.d.ts +7 -0
  79. package/mjs/HttpException.js +15 -0
  80. package/mjs/JSONLinesResponse.d.ts +19 -0
  81. package/mjs/JSONLinesResponse.js +93 -0
  82. package/mjs/VovkApp.d.ts +29 -0
  83. package/mjs/VovkApp.js +202 -0
  84. package/mjs/client/createRPC.d.ts +3 -0
  85. package/mjs/client/createRPC.js +90 -0
  86. package/mjs/client/defaultHandler.d.ts +6 -0
  87. package/mjs/client/defaultHandler.js +29 -0
  88. package/mjs/client/defaultStreamHandler.d.ts +9 -0
  89. package/mjs/client/defaultStreamHandler.js +94 -0
  90. package/mjs/client/fetcher.d.ts +14 -0
  91. package/mjs/client/fetcher.js +93 -0
  92. package/mjs/client/index.d.ts +4 -0
  93. package/mjs/client/index.js +10 -0
  94. package/mjs/client/progressive.d.ts +9 -0
  95. package/mjs/client/progressive.js +45 -0
  96. package/mjs/client/types.d.ts +123 -0
  97. package/mjs/createVovkApp.d.ts +63 -0
  98. package/mjs/createVovkApp.js +133 -0
  99. package/mjs/index.d.ts +65 -0
  100. package/mjs/index.js +38 -0
  101. package/mjs/openapi/error.d.ts +2 -0
  102. package/mjs/openapi/error.js +100 -0
  103. package/mjs/openapi/generateFnName.d.ts +23 -0
  104. package/mjs/openapi/generateFnName.js +81 -0
  105. package/mjs/openapi/index.d.ts +12 -0
  106. package/mjs/openapi/index.js +21 -0
  107. package/mjs/openapi/openAPIToVovkSchema/applyComponentsSchemas.d.ts +3 -0
  108. package/mjs/openapi/openAPIToVovkSchema/applyComponentsSchemas.js +67 -0
  109. package/mjs/openapi/openAPIToVovkSchema/index.d.ts +4 -0
  110. package/mjs/openapi/openAPIToVovkSchema/index.js +192 -0
  111. package/mjs/openapi/openAPIToVovkSchema/inlineRefs.d.ts +10 -0
  112. package/mjs/openapi/openAPIToVovkSchema/inlineRefs.js +102 -0
  113. package/mjs/openapi/vovkSchemaToOpenAPI.d.ts +9 -0
  114. package/mjs/openapi/vovkSchemaToOpenAPI.js +233 -0
  115. package/mjs/types.d.ts +400 -0
  116. package/mjs/types.js +74 -0
  117. package/mjs/utils/camelCase.d.ts +6 -0
  118. package/mjs/utils/camelCase.js +37 -0
  119. package/mjs/utils/createCodeExamples.d.ts +19 -0
  120. package/mjs/utils/createCodeExamples.js +110 -0
  121. package/mjs/utils/createDecorator.d.ts +6 -0
  122. package/mjs/utils/createDecorator.js +48 -0
  123. package/mjs/utils/createLLMTools.d.ts +44 -0
  124. package/mjs/utils/createLLMTools.js +118 -0
  125. package/mjs/utils/createStandardValidation.d.ts +81 -0
  126. package/mjs/utils/createStandardValidation.js +33 -0
  127. package/mjs/utils/generateStaticAPI.d.ts +4 -0
  128. package/mjs/utils/generateStaticAPI.js +30 -0
  129. package/mjs/utils/getJSONSchemaExample.d.ts +8 -0
  130. package/mjs/utils/getJSONSchemaExample.js +234 -0
  131. package/mjs/utils/getJSONSchemaSample.d.ts +2 -0
  132. package/mjs/utils/getJSONSchemaSample.js +167 -0
  133. package/mjs/utils/getSchema.d.ts +21 -0
  134. package/mjs/utils/getSchema.js +38 -0
  135. package/mjs/utils/multitenant.d.ts +24 -0
  136. package/mjs/utils/multitenant.js +170 -0
  137. package/mjs/utils/parseQuery.d.ts +25 -0
  138. package/mjs/utils/parseQuery.js +156 -0
  139. package/mjs/utils/reqForm.d.ts +2 -0
  140. package/mjs/utils/reqForm.js +33 -0
  141. package/mjs/utils/reqMeta.d.ts +2 -0
  142. package/mjs/utils/reqMeta.js +13 -0
  143. package/mjs/utils/reqQuery.d.ts +2 -0
  144. package/mjs/utils/reqQuery.js +10 -0
  145. package/mjs/utils/serializeQuery.d.ts +13 -0
  146. package/mjs/utils/serializeQuery.js +65 -0
  147. package/mjs/utils/setHandlerSchema.d.ts +4 -0
  148. package/mjs/utils/setHandlerSchema.js +15 -0
  149. package/mjs/utils/shim.d.ts +1 -0
  150. package/mjs/utils/shim.js +18 -0
  151. package/mjs/utils/upperFirst.d.ts +1 -0
  152. package/mjs/utils/upperFirst.js +6 -0
  153. package/mjs/utils/withValidationLibrary.d.ts +76 -0
  154. package/mjs/utils/withValidationLibrary.js +123 -0
  155. package/package.json +29 -6
  156. package/.npmignore +0 -2
  157. package/StreamJSONResponse.d.ts +0 -17
  158. package/StreamJSONResponse.js +0 -54
  159. package/VovkApp.js +0 -185
  160. package/client/clientizeController.d.ts +0 -4
  161. package/client/clientizeController.js +0 -92
  162. package/client/defaultFetcher.d.ts +0 -4
  163. package/client/defaultFetcher.js +0 -49
  164. package/client/defaultHandler.d.ts +0 -2
  165. package/client/defaultHandler.js +0 -22
  166. package/client/defaultStreamHandler.d.ts +0 -4
  167. package/client/index.d.ts +0 -4
  168. package/client/index.js +0 -5
  169. package/client/types.d.ts +0 -100
  170. package/createDecorator.d.ts +0 -4
  171. package/createVovkApp.js +0 -118
  172. package/index.d.ts +0 -60
  173. package/index.js +0 -20
  174. package/types.d.ts +0 -157
  175. package/types.js +0 -65
  176. package/utils/generateStaticAPI.d.ts +0 -4
  177. package/utils/generateStaticAPI.js +0 -18
  178. package/utils/getSchema.d.ts +0 -8
  179. package/utils/getSchema.js +0 -38
  180. package/utils/reqForm.js +0 -13
  181. package/utils/reqQuery.d.ts +0 -3
  182. package/utils/reqQuery.js +0 -25
  183. package/utils/setClientValidatorsForHandler.d.ts +0 -5
  184. package/utils/setClientValidatorsForHandler.js +0 -25
  185. package/worker/index.d.ts +0 -3
  186. package/worker/index.js +0 -7
  187. package/worker/promisifyWorker.d.ts +0 -2
  188. package/worker/promisifyWorker.js +0 -141
  189. package/worker/types.d.ts +0 -31
  190. package/worker/worker.d.ts +0 -1
  191. package/worker/worker.js +0 -43
  192. /package/{client → cjs/client}/types.js +0 -0
  193. /package/{utils → cjs/utils}/reqMeta.js +0 -0
  194. /package/{utils → cjs/utils}/shim.d.ts +0 -0
  195. /package/{utils → cjs/utils}/shim.js +0 -0
  196. /package/{worker → mjs/client}/types.js +0 -0
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getJSONSchemaSample = getJSONSchemaSample;
4
+ function getJSONSchemaSample(schema, rootSchema) {
5
+ if (!schema || typeof schema !== 'object')
6
+ return null;
7
+ // Use the input schema as the root if not provided
8
+ rootSchema = rootSchema || schema;
9
+ // If there's an example, use it
10
+ if (schema.example !== undefined) {
11
+ return schema.example;
12
+ }
13
+ // If there are examples, use one of them
14
+ if (schema.examples && schema.examples.length > 0) {
15
+ return schema.examples[0];
16
+ }
17
+ // Handle const if present
18
+ if (schema.const !== undefined) {
19
+ return schema.const;
20
+ }
21
+ // Handle $ref if present
22
+ if (schema.$ref) {
23
+ return handleRef(schema.$ref, rootSchema);
24
+ }
25
+ // Handle enum if present
26
+ if (schema.enum && schema.enum.length > 0) {
27
+ return schema.enum[0];
28
+ }
29
+ // Handle oneOf, anyOf, allOf
30
+ if (schema.oneOf && schema.oneOf.length > 0) {
31
+ return getJSONSchemaSample(schema.oneOf[0], rootSchema);
32
+ }
33
+ if (schema.anyOf && schema.anyOf.length > 0) {
34
+ return getJSONSchemaSample(schema.anyOf[0], rootSchema);
35
+ }
36
+ if (schema.allOf && schema.allOf.length > 0) {
37
+ // Merge all schemas in allOf
38
+ const mergedSchema = schema.allOf.reduce((acc, s) => ({ ...acc, ...s }), {});
39
+ return getJSONSchemaSample(mergedSchema, rootSchema);
40
+ }
41
+ // Handle different types
42
+ if (schema.type) {
43
+ switch (schema.type) {
44
+ case 'string':
45
+ return handleString(schema);
46
+ case 'number':
47
+ case 'integer':
48
+ return handleNumber(schema);
49
+ case 'boolean':
50
+ return handleBoolean();
51
+ case 'object':
52
+ return handleObject(schema, rootSchema);
53
+ case 'array':
54
+ return handleArray(schema, rootSchema);
55
+ case 'null':
56
+ return null;
57
+ default:
58
+ return null;
59
+ }
60
+ }
61
+ // If type is not specified but properties are, treat it as an object
62
+ if (schema.properties) {
63
+ return handleObject(schema, rootSchema);
64
+ }
65
+ // Default fallback
66
+ return null;
67
+ }
68
+ function handleRef(ref, rootSchema) {
69
+ // Parse the reference path
70
+ const path = ref.split('/').slice(1); // Remove the initial '#'
71
+ // Navigate through the schema to find the referenced definition
72
+ let current = rootSchema;
73
+ for (const segment of path) {
74
+ current = current[segment];
75
+ if (current === undefined) {
76
+ return null; // Reference not found
77
+ }
78
+ }
79
+ // Process the referenced schema
80
+ return getJSONSchemaSample(current, rootSchema);
81
+ }
82
+ function handleString(schema) {
83
+ if (schema.format) {
84
+ switch (schema.format) {
85
+ case 'email':
86
+ case 'idn-email':
87
+ return 'user@example.com';
88
+ case 'uri':
89
+ case 'url':
90
+ case 'iri':
91
+ return 'https://example.com';
92
+ case 'date':
93
+ return '2023-01-01';
94
+ case 'date-time':
95
+ return '2023-01-01T00:00:00Z';
96
+ case 'time':
97
+ return '12:00:00Z';
98
+ case 'duration':
99
+ return 'PT1H';
100
+ case 'uuid':
101
+ return '00000000-0000-0000-0000-000000000000';
102
+ case 'regex':
103
+ return '^[a-zA-Z0-9]+$';
104
+ case 'relative-json-pointer':
105
+ return '/some/relative/path';
106
+ case 'color':
107
+ return '#000000';
108
+ case 'hostname':
109
+ return 'example.com';
110
+ case 'zipcode':
111
+ return '12345';
112
+ case 'phone':
113
+ return '+123-456-7890';
114
+ case 'password':
115
+ return '******';
116
+ default:
117
+ return 'string';
118
+ }
119
+ }
120
+ if (schema.pattern) {
121
+ // For simplicity, return a basic string for patterns
122
+ return 'pattern-string';
123
+ }
124
+ return 'string';
125
+ }
126
+ function handleNumber(schema) {
127
+ if (schema.minimum !== undefined && schema.maximum !== undefined) {
128
+ return schema.minimum;
129
+ }
130
+ else if (schema.minimum !== undefined) {
131
+ return schema.minimum;
132
+ }
133
+ else if (schema.maximum !== undefined) {
134
+ return schema.maximum;
135
+ }
136
+ return 0;
137
+ }
138
+ function handleBoolean() {
139
+ return true;
140
+ }
141
+ function handleObject(schema, rootSchema) {
142
+ const result = {};
143
+ if (schema.properties) {
144
+ const required = schema.required || [];
145
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
146
+ // Only include required properties or as a basic example
147
+ if (required.includes(key) || required.length === 0) {
148
+ result[key] = getJSONSchemaSample(propSchema, rootSchema);
149
+ }
150
+ }
151
+ }
152
+ // Handle additionalProperties
153
+ if (schema.additionalProperties && typeof schema.additionalProperties === 'object') {
154
+ result['additionalProp'] = getJSONSchemaSample(schema.additionalProperties, rootSchema);
155
+ }
156
+ return result;
157
+ }
158
+ function handleArray(schema, rootSchema) {
159
+ if (schema.items) {
160
+ const itemSchema = schema.items;
161
+ const minItems = schema.minItems || 1;
162
+ // Create minimum number of items (capped at a reasonable max for examples)
163
+ const numItems = Math.min(minItems, 3);
164
+ return Array.from({ length: numItems }, () => getJSONSchemaSample(itemSchema, rootSchema));
165
+ }
166
+ return [];
167
+ }
@@ -0,0 +1,21 @@
1
+ import { type VovkSegmentSchema, type VovkController, type StaticClass } from '../types';
2
+ export declare function getControllerSchema(controller: VovkController, rpcModuleName: string, exposeValidation: boolean): Promise<{
3
+ rpcModuleName: string;
4
+ originalControllerName: string;
5
+ prefix: string;
6
+ handlers: {
7
+ [k: string]: {
8
+ path: string;
9
+ httpMethod: string;
10
+ openapi?: import("openapi3-ts/oas31").OperationObject;
11
+ misc?: Record<string, import("../types").KnownAny>;
12
+ };
13
+ };
14
+ }>;
15
+ export default function getSchema(options: {
16
+ emitSchema?: boolean;
17
+ segmentName?: string;
18
+ controllers: Record<string, StaticClass>;
19
+ exposeValidation?: boolean;
20
+ forceApiRoot?: string;
21
+ }): Promise<VovkSegmentSchema>;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getControllerSchema = getControllerSchema;
4
+ exports.default = getSchema;
5
+ const types_1 = require("../types");
6
+ async function getControllerSchema(controller, rpcModuleName, exposeValidation) {
7
+ const handlers = exposeValidation
8
+ ? controller._handlers
9
+ : Object.fromEntries(
10
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
11
+ Object.entries(controller._handlers ?? {}).map(([key, { validation: _v, ...value }]) => [key, value]));
12
+ return {
13
+ rpcModuleName,
14
+ originalControllerName: controller.name,
15
+ prefix: controller._prefix ?? '',
16
+ handlers,
17
+ };
18
+ }
19
+ async function getSchema(options) {
20
+ const exposeValidation = options?.exposeValidation ?? true;
21
+ const emitSchema = options.emitSchema ?? true;
22
+ const schema = {
23
+ $schema: types_1.VovkSchemaIdEnum.SEGMENT,
24
+ emitSchema,
25
+ segmentName: options.segmentName ?? '',
26
+ segmentType: 'segment',
27
+ controllers: {},
28
+ };
29
+ if (options.forceApiRoot) {
30
+ schema.forceApiRoot = options.forceApiRoot;
31
+ }
32
+ if (!emitSchema)
33
+ return schema;
34
+ for (const [rpcModuleName, controller] of Object.entries(options.controllers ?? {})) {
35
+ schema.controllers[rpcModuleName] = await getControllerSchema(controller, rpcModuleName, exposeValidation);
36
+ }
37
+ return schema;
38
+ }
@@ -0,0 +1,24 @@
1
+ type Override = {
2
+ from: string;
3
+ to: string;
4
+ };
5
+ type Config = {
6
+ requestUrl: string;
7
+ requestHost: string;
8
+ targetHost: string;
9
+ overrides: {
10
+ [key: string]: Override[];
11
+ };
12
+ };
13
+ export declare function multitenant(config: Config): {
14
+ action: null;
15
+ destination: null;
16
+ message: string;
17
+ subdomains: null;
18
+ } | {
19
+ action: string;
20
+ destination: string;
21
+ message: string;
22
+ subdomains: Record<string, string> | null;
23
+ };
24
+ export {};
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ /*
3
+ Function "multitenant" accepts an object with "requestUrl", "requestHost", "targetHost" and "overrides" properties.
4
+ The requestHost parameter comes from request headers and should be used instead of the hostname in the requestUrl.
5
+ The keys in overrides are subdomains relative to the "targetHost". They can include a dot to indicate lower-level
6
+ subdomains, and can use square brackets to indicate wildcard values. The values are arrays of objects with "from"
7
+ and "to" properties, which are strings indicating the override paths for Next.js.
8
+
9
+ If a path matches a specific subdomain name (e.g., "/admin" matches the "admin" subdomain),
10
+ the request will be redirected to that specific subdomain instead of being processed on the current subdomain.
11
+
12
+ If a path ends with "_schema_", it will not be processed by any overrides and will be passed through as-is.
13
+
14
+ Example:
15
+
16
+ const { action, destination, message, subdomains } = multitenant({
17
+ requestUrl: request.url,
18
+ requestHost: request.headers.get('host'),
19
+ targetHost: "localhost:3000",
20
+ overrides: {
21
+ "[customer_name].customer": [
22
+ { from: "api", to: "api/customer" }, // API
23
+ { from: "", to: "[customer_name]" } // UI
24
+ ],
25
+ "pro.[customer_name].customer": [
26
+ { from: "api", to: "api/customer/pro" }, // API
27
+ { from: "", to: "pro/[customer_name]" } // UI
28
+ ],
29
+ "admin": [
30
+ { from: "api", to: "api/admin" }, // API
31
+ { from: "", to: "admin" } // UI
32
+ ],
33
+ },
34
+ });
35
+
36
+ subdomains is a Record<string, string> | null where keys are subdomain names and values are their corresponding values. If non-wildcard subdomains are used, should be equal to null.
37
+ action is one of "rewrite", "redirect", "notfound", or null.
38
+ message is a string describing the action taken
39
+ destination is a string URL to redirect or rewrite to, or null if no action is taken or "notfound"
40
+ */
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.multitenant = multitenant;
43
+ // Get the reserved paths from the overrides configuration
44
+ const getReservedPaths = (overrides) => {
45
+ return Object.keys(overrides).filter((key) => !key.includes('[') && !key.includes(']')); // Filter out dynamic paths
46
+ };
47
+ /**
48
+ * Convert a pattern with [placeholders] to a regex pattern and extract placeholder names
49
+ */
50
+ const patternToRegex = (pattern) => {
51
+ const paramNames = [];
52
+ const regexPattern = pattern
53
+ .replace(/\[([^\]]+)\]/g, (_, name) => {
54
+ paramNames.push(name);
55
+ return '([^.]+)';
56
+ })
57
+ .replace(/\./g, '\\.'); // Escape dots in the pattern
58
+ return {
59
+ regex: new RegExp(`^${regexPattern}$`),
60
+ paramNames,
61
+ };
62
+ };
63
+ function multitenant(config) {
64
+ const { requestUrl, requestHost, targetHost, overrides } = config;
65
+ // Parse the URL
66
+ const urlObj = new URL(requestUrl);
67
+ const pathname = urlObj.pathname.slice(1); // Remove leading slash
68
+ // Skip processing for paths ending with "_schema_"
69
+ if (pathname.endsWith('_schema_')) {
70
+ return {
71
+ action: null,
72
+ destination: null,
73
+ message: 'Schema endpoint, bypassing overrides',
74
+ subdomains: null,
75
+ };
76
+ }
77
+ const pathSegments = pathname.split('/').filter(Boolean);
78
+ // Get reserved paths
79
+ const reservedPaths = getReservedPaths(overrides);
80
+ // Check if any path segment matches a reserved path (e.g., "admin")
81
+ for (let i = 0; i < pathSegments.length; i++) {
82
+ const segment = pathSegments[i];
83
+ if (reservedPaths.includes(segment)) {
84
+ // Create the destination URL with the reserved path as subdomain
85
+ const destinationHost = `${segment}.${targetHost}`;
86
+ // Keep path segments before the reserved path
87
+ const beforeSegments = pathSegments.slice(0, i);
88
+ // Keep path segments after the reserved path
89
+ const afterSegments = pathSegments.slice(i + 1);
90
+ // Construct the new path
91
+ const newPath = [...beforeSegments, ...afterSegments].join('/');
92
+ const destinationUrl = new URL(`${urlObj.protocol}//${destinationHost}`);
93
+ if (newPath) {
94
+ destinationUrl.pathname = `/${newPath}`;
95
+ }
96
+ // Keep any query parameters
97
+ destinationUrl.search = urlObj.search;
98
+ return {
99
+ action: 'redirect',
100
+ destination: destinationUrl.toString(),
101
+ message: `Redirecting to ${segment} subdomain`,
102
+ subdomains: null, // No wildcards used
103
+ };
104
+ }
105
+ }
106
+ // Process based on host and subdomains
107
+ for (const pattern in overrides) {
108
+ const fullPattern = `${pattern}.${targetHost}`;
109
+ const { regex, paramNames } = patternToRegex(fullPattern);
110
+ const match = requestHost.match(regex);
111
+ if (match) {
112
+ const overrideRules = overrides[pattern];
113
+ // Extract parameters from the match
114
+ const params = {};
115
+ if (match.length > 1) {
116
+ for (let i = 0; i < paramNames.length; i++) {
117
+ params[paramNames[i]] = match[i + 1];
118
+ }
119
+ }
120
+ // Find the appropriate rule based on the path
121
+ for (const rule of overrideRules) {
122
+ if (pathname === rule.from || pathname.startsWith(`${rule.from}/`)) {
123
+ // Replace path with the destination
124
+ let destination = pathname.replace(rule.from, rule.to);
125
+ // Replace any dynamic parameters in destination
126
+ if (Object.keys(params).length > 0) {
127
+ Object.entries(params).forEach(([key, value]) => {
128
+ destination = destination.replace(`[${key}]`, value);
129
+ });
130
+ }
131
+ // Only return non-null subdomains if we have wildcard parameters
132
+ const wildcardSubdomains = paramNames.length > 0 ? params : null;
133
+ return {
134
+ action: 'rewrite',
135
+ destination: `${urlObj.protocol}//${urlObj.host}/${destination}${urlObj.search}`,
136
+ message: `Rewriting to ${destination}`,
137
+ subdomains: wildcardSubdomains,
138
+ };
139
+ }
140
+ }
141
+ }
142
+ }
143
+ // Handle cases where a customer subdomain tries to access reserved paths
144
+ if (pathSegments.length > 0 && reservedPaths.includes(pathSegments[0])) {
145
+ const reservedPath = pathSegments[0];
146
+ const restPath = pathSegments.slice(1).join('/');
147
+ // Create the destination URL with the reserved path as subdomain
148
+ const destinationHost = `${reservedPath}.${targetHost}`;
149
+ const destinationUrl = new URL(`${urlObj.protocol}//${destinationHost}`);
150
+ // Only add remaining path segments if they exist
151
+ if (restPath) {
152
+ destinationUrl.pathname = `/${restPath}`;
153
+ }
154
+ // Keep any query parameters
155
+ destinationUrl.search = urlObj.search;
156
+ return {
157
+ action: 'redirect',
158
+ destination: destinationUrl.toString(),
159
+ message: `Redirecting to ${reservedPath} subdomain`,
160
+ subdomains: null, // No wildcards used for reserved paths
161
+ };
162
+ }
163
+ // Default case - pass through
164
+ return {
165
+ action: null,
166
+ destination: null,
167
+ message: 'No action',
168
+ subdomains: null, // No wildcards matched
169
+ };
170
+ }
@@ -0,0 +1,25 @@
1
+ import type { KnownAny } from '../types';
2
+ /**
3
+ * Deserialize a bracket-based query string into an object.
4
+ *
5
+ * Supports:
6
+ * - Key/value pairs with nested brackets (e.g. "a[b][0]=value")
7
+ * - Arrays with empty bracket (e.g. "arr[]=1&arr[]=2")
8
+ * - Mixed arrays of objects, etc.
9
+ *
10
+ * @example
11
+ * parseQuery("x=xx&y[0]=yy&y[1]=uu&z[f]=x&z[u][0]=uu&z[u][1]=xx&z[d][x]=ee")
12
+ * => {
13
+ * x: "xx",
14
+ * y: ["yy", "uu"],
15
+ * z: {
16
+ * f: "x",
17
+ * u: ["uu", "xx"],
18
+ * d: { x: "ee" }
19
+ * }
20
+ * }
21
+ *
22
+ * @param queryString - The raw query string (e.g. location.search.slice(1))
23
+ * @returns - A nested object representing the query params
24
+ */
25
+ export default function parseQuery(queryString: string): Record<string, KnownAny>;
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = parseQuery;
4
+ /**
5
+ * Parse a bracket-based key (e.g. "z[d][0][x]" or "arr[]")
6
+ * into an array of path segments (strings or special push-markers).
7
+ *
8
+ * Example: "z[d][0][x]" => ["z", "d", "0", "x"]
9
+ * Example: "arr[]" => ["arr", "" ] // "" indicates "push" onto array
10
+ */
11
+ function parseKey(key) {
12
+ // The first segment is everything up to the first '[' (or the entire key if no '[')
13
+ const segments = [];
14
+ const topKeyMatch = key.match(/^([^[\]]+)/);
15
+ if (topKeyMatch) {
16
+ segments.push(topKeyMatch[1]);
17
+ }
18
+ else {
19
+ // If it starts with brackets, treat it as empty? (edge case)
20
+ segments.push('');
21
+ }
22
+ // Now capture all bracket parts: [something], [0], []
23
+ const bracketRegex = /\[([^[\]]*)\]/g;
24
+ let match;
25
+ while ((match = bracketRegex.exec(key)) !== null) {
26
+ // match[1] is the content inside the brackets
27
+ segments.push(match[1]);
28
+ }
29
+ return segments;
30
+ }
31
+ /**
32
+ * Recursively set a value in a nested object/array, given a path of segments.
33
+ * - If segment is numeric => treat as array index
34
+ * - If segment is empty "" => push to array
35
+ * - Else => object property
36
+ */
37
+ function setValue(obj, path, value) {
38
+ let current = obj;
39
+ for (let i = 0; i < path.length; i++) {
40
+ const segment = path[i];
41
+ // If we're at the last segment, set the value
42
+ if (i === path.length - 1) {
43
+ if (segment === '') {
44
+ // Empty bracket => push
45
+ if (!Array.isArray(current)) {
46
+ current = [];
47
+ }
48
+ current.push(value);
49
+ }
50
+ else if (!isNaN(Number(segment))) {
51
+ // Numeric segment => array index
52
+ const idx = Number(segment);
53
+ if (!Array.isArray(current)) {
54
+ current = [];
55
+ }
56
+ current[idx] = value;
57
+ }
58
+ else {
59
+ // Object property
60
+ current[segment] = value;
61
+ }
62
+ }
63
+ else {
64
+ // Not the last segment: descend into existing structure or create it
65
+ const nextSegment = path[i + 1];
66
+ if (segment === '') {
67
+ // Empty bracket => push
68
+ if (!Array.isArray(current)) {
69
+ // Convert the current node into an array, if not one
70
+ current = [];
71
+ }
72
+ // If we are not at the last path, we need a placeholder object or array
73
+ // for the next segment. We'll push something and move current to that.
74
+ if (current.length === 0) {
75
+ // nothing in array yet
76
+ current.push(typeof nextSegment === 'string' && !isNaN(Number(nextSegment)) ? [] : {});
77
+ }
78
+ else if (typeof nextSegment === 'string' && !isNaN(Number(nextSegment))) {
79
+ // next is numeric => we want an array
80
+ if (!Array.isArray(current[current.length - 1])) {
81
+ current[current.length - 1] = [];
82
+ }
83
+ }
84
+ else {
85
+ // next is not numeric => we want an object
86
+ if (typeof current[current.length - 1] !== 'object') {
87
+ current[current.length - 1] = {};
88
+ }
89
+ }
90
+ current = current[current.length - 1];
91
+ }
92
+ else if (!isNaN(Number(segment))) {
93
+ // segment is numeric => array index
94
+ const idx = Number(segment);
95
+ if (!Array.isArray(current)) {
96
+ current = [];
97
+ }
98
+ if (current[idx] === undefined) {
99
+ // Create placeholder for next segment
100
+ current[idx] = typeof nextSegment === 'string' && !isNaN(Number(nextSegment)) ? [] : {};
101
+ }
102
+ current = current[idx];
103
+ }
104
+ else {
105
+ // segment is an object key
106
+ if (current[segment] === undefined) {
107
+ // Create placeholder
108
+ current[segment] = typeof nextSegment === 'string' && !isNaN(Number(nextSegment)) ? [] : {};
109
+ }
110
+ current = current[segment];
111
+ }
112
+ }
113
+ }
114
+ }
115
+ /**
116
+ * Deserialize a bracket-based query string into an object.
117
+ *
118
+ * Supports:
119
+ * - Key/value pairs with nested brackets (e.g. "a[b][0]=value")
120
+ * - Arrays with empty bracket (e.g. "arr[]=1&arr[]=2")
121
+ * - Mixed arrays of objects, etc.
122
+ *
123
+ * @example
124
+ * parseQuery("x=xx&y[0]=yy&y[1]=uu&z[f]=x&z[u][0]=uu&z[u][1]=xx&z[d][x]=ee")
125
+ * => {
126
+ * x: "xx",
127
+ * y: ["yy", "uu"],
128
+ * z: {
129
+ * f: "x",
130
+ * u: ["uu", "xx"],
131
+ * d: { x: "ee" }
132
+ * }
133
+ * }
134
+ *
135
+ * @param queryString - The raw query string (e.g. location.search.slice(1))
136
+ * @returns - A nested object representing the query params
137
+ */
138
+ function parseQuery(queryString) {
139
+ const result = {};
140
+ if (!queryString)
141
+ return result;
142
+ // Split into key=value pairs
143
+ const pairs = queryString
144
+ .replace(/^\?/, '') // Remove leading "?" if present
145
+ .split('&');
146
+ for (const pair of pairs) {
147
+ const [rawKey, rawVal = ''] = pair.split('=');
148
+ const decodedKey = decodeURIComponent(rawKey);
149
+ const decodedVal = decodeURIComponent(rawVal);
150
+ // Parse bracket notation
151
+ const pathSegments = parseKey(decodedKey);
152
+ // Insert into the result object
153
+ setValue(result, pathSegments, decodedVal);
154
+ }
155
+ return result;
156
+ }
@@ -1,3 +1,2 @@
1
- import { VovkRequest } from 'vovk';
2
- import { _KnownAny as KnownAny } from '../types';
1
+ import type { KnownAny, VovkRequest } from '../types';
3
2
  export default function reqForm<T = KnownAny>(req: VovkRequest<KnownAny, KnownAny>): Promise<T>;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = reqForm;
4
+ const formMap = new WeakMap();
5
+ async function reqForm(req) {
6
+ if (formMap.has(req)) {
7
+ return formMap.get(req);
8
+ }
9
+ const body = await req.formData();
10
+ req.formData = () => Promise.resolve(body);
11
+ const formData = {};
12
+ for (const [key, value] of body.entries()) {
13
+ if (value instanceof File) {
14
+ // If this key already exists, convert to array or append to existing array
15
+ if (formData[key]) {
16
+ if (Array.isArray(formData[key])) {
17
+ formData[key].push(value);
18
+ }
19
+ else {
20
+ formData[key] = [formData[key], value];
21
+ }
22
+ }
23
+ else {
24
+ formData[key] = value;
25
+ }
26
+ }
27
+ else {
28
+ formData[key] = value.toString();
29
+ }
30
+ }
31
+ formMap.set(req, formData);
32
+ return formData;
33
+ }
@@ -1,3 +1,2 @@
1
- import { VovkRequest } from 'vovk';
2
- import { _KnownAny as KnownAny } from '../types';
1
+ import type { KnownAny, VovkRequest } from '../types';
3
2
  export default function reqMeta<T = Record<KnownAny, KnownAny>>(req: VovkRequest<KnownAny, KnownAny>, meta?: T | null): T;
@@ -0,0 +1,2 @@
1
+ import type { VovkRequest } from '../types';
2
+ export default function reqQuery<T extends object | undefined>(req: VovkRequest<unknown, T>): T;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = reqQuery;
7
+ const parseQuery_1 = __importDefault(require("./parseQuery"));
8
+ function reqQuery(req) {
9
+ return (0, parseQuery_1.default)(req.nextUrl.search);
10
+ }
@@ -0,0 +1,13 @@
1
+ import type { KnownAny } from '../types';
2
+ /**
3
+ * Serialize a nested object (including arrays, arrays of objects, etc.)
4
+ * into a bracket-based query string.
5
+ *
6
+ * @example
7
+ * serializeQuery({ x: 'xx', y: [1, 2], z: { f: 'x' } })
8
+ * => "x=xx&y[0]=1&y[1]=2&z[f]=x"
9
+ *
10
+ * @param obj - The input object to be serialized
11
+ * @returns - A bracket-based query string (without leading "?")
12
+ */
13
+ export default function serializeQuery(obj: Record<string, KnownAny>): string;