silgi 0.14.4 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- const version = "0.14.4";
1
+ const version = "0.16.0";
2
2
  const peerDependencies = {
3
3
  "@fastify/deepmerge": "^2.0.2",
4
4
  "@nuxt/kit": "^3.15.3",
@@ -0,0 +1,274 @@
1
+ function patternToRegex(pattern) {
2
+ let regexStr = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
3
+ regexStr = regexStr.replace(/\*\*/g, ".+");
4
+ regexStr = regexStr.replace(/\*/g, "[^/]+");
5
+ regexStr = regexStr.replace(/:([^/]+)/g, "([^/]+)");
6
+ return new RegExp(`^${regexStr}$`);
7
+ }
8
+ function extractParams(url, pattern) {
9
+ if (!pattern.includes(":"))
10
+ return null;
11
+ const paramNames = [];
12
+ const patternRegex = pattern.replace(/:([^/]+)/g, (_, name) => {
13
+ paramNames.push(name);
14
+ return "([^/]+)";
15
+ });
16
+ const regex = new RegExp(`^${patternRegex}$`);
17
+ const matches = url.match(regex);
18
+ if (!matches)
19
+ return null;
20
+ const params = {};
21
+ paramNames.forEach((name, i) => {
22
+ params[name] = matches[i + 1];
23
+ });
24
+ return Object.keys(params).length > 0 ? params : null;
25
+ }
26
+ function matchesPattern(url, pattern) {
27
+ if (url === pattern)
28
+ return true;
29
+ if (url.endsWith("/") && !pattern.endsWith("/") || !url.endsWith("/") && pattern.endsWith("/")) {
30
+ return false;
31
+ }
32
+ if (pattern.endsWith("/**")) {
33
+ const basePath = pattern.slice(0, -3);
34
+ return url === basePath || url.startsWith(`${basePath}/`);
35
+ }
36
+ const urlParts = url.split("/");
37
+ const patternParts = pattern.split("/");
38
+ if (!pattern.includes("**") && urlParts.length !== patternParts.length) {
39
+ return false;
40
+ }
41
+ if (pattern.includes("/**/")) {
42
+ const [prefix, ...suffixParts] = pattern.split("/**/");
43
+ const suffix = suffixParts.join("/");
44
+ return url.startsWith(prefix) && (!suffix || url.endsWith(`/${suffix}`));
45
+ }
46
+ return patternToRegex(pattern).test(url);
47
+ }
48
+ function deepClone(obj) {
49
+ if (obj === null || typeof obj !== "object")
50
+ return obj;
51
+ if (typeof obj === "function")
52
+ return obj;
53
+ if (Array.isArray(obj))
54
+ return obj.map(deepClone);
55
+ const cloned = {};
56
+ for (const key in obj) {
57
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
58
+ cloned[key] = deepClone(obj[key]);
59
+ }
60
+ }
61
+ return cloned;
62
+ }
63
+ function mergeConfigs(...configs) {
64
+ const result = {};
65
+ for (const config of configs) {
66
+ if (!config)
67
+ continue;
68
+ for (const key in config) {
69
+ const value = config[key];
70
+ if (value && typeof value === "object" && !Array.isArray(value) && result[key] && typeof result[key] === "object" && typeof result[key] !== "function") {
71
+ const resultValue = result[key];
72
+ const valueToMerge = value;
73
+ result[key] = mergeConfigs(resultValue, valueToMerge);
74
+ } else {
75
+ result[key] = value;
76
+ }
77
+ }
78
+ }
79
+ return result;
80
+ }
81
+ function createRouteRules() {
82
+ let _rules = {};
83
+ let _mergedRules = {};
84
+ let _rulesModified = false;
85
+ function getRules() {
86
+ return deepClone(_rules);
87
+ }
88
+ function importRules(config) {
89
+ _rules = deepClone(config);
90
+ _rulesModified = true;
91
+ updateMergeRules();
92
+ }
93
+ function exportRules() {
94
+ return deepClone(_rules);
95
+ }
96
+ function addRule(pattern, config) {
97
+ _rules[pattern] = deepClone(config);
98
+ _rulesModified = true;
99
+ updateMergeRules();
100
+ }
101
+ function updateRule(pattern, config) {
102
+ if (_rules[pattern]) {
103
+ _rules[pattern] = { ..._rules[pattern], ...deepClone(config) };
104
+ } else {
105
+ _rules[pattern] = deepClone(config);
106
+ }
107
+ _rulesModified = true;
108
+ updateMergeRules();
109
+ }
110
+ function removeRule(pattern) {
111
+ delete _rules[pattern];
112
+ _rulesModified = true;
113
+ updateMergeRules();
114
+ }
115
+ function matchesRule(url, pattern) {
116
+ return matchesPattern(url, pattern);
117
+ }
118
+ function getMatchingPatterns(url) {
119
+ return Object.keys(_rules).filter((pattern) => matchesPattern(url, pattern)).sort((a, b) => {
120
+ if (a === url)
121
+ return -1;
122
+ if (b === url)
123
+ return 1;
124
+ const aSegments = a.split("/").filter(Boolean).length;
125
+ const bSegments = b.split("/").filter(Boolean).length;
126
+ if (aSegments !== bSegments)
127
+ return bSegments - aSegments;
128
+ const aWildcards = (a.match(/\*/g) || []).length;
129
+ const bWildcards = (b.match(/\*/g) || []).length;
130
+ if (aWildcards !== bWildcards)
131
+ return aWildcards - bWildcards;
132
+ const aDoubleWildcards = (a.match(/\*\*/g) || []).length;
133
+ const bDoubleWildcards = (b.match(/\*\*/g) || []).length;
134
+ if (aDoubleWildcards !== bDoubleWildcards)
135
+ return aDoubleWildcards - bDoubleWildcards;
136
+ return b.length - a.length;
137
+ });
138
+ }
139
+ function getConfig(url) {
140
+ if (_rulesModified) {
141
+ updateMergeRules();
142
+ }
143
+ if (_mergedRules[url]) {
144
+ return deepClone(_mergedRules[url]);
145
+ }
146
+ const patterns = getMatchingPatterns(url);
147
+ if (!patterns.length)
148
+ return null;
149
+ const mergedConfig = {};
150
+ for (let i = patterns.length - 1; i >= 0; i--) {
151
+ const pattern = patterns[i];
152
+ const patternConfig = _mergedRules[pattern] || _rules[pattern];
153
+ Object.assign(mergedConfig, deepClone(patternConfig));
154
+ }
155
+ _mergedRules[url] = deepClone(mergedConfig);
156
+ return mergedConfig;
157
+ }
158
+ function computeMergedConfig(url, patterns) {
159
+ const matchingPatterns = patterns || getMatchingPatterns(url);
160
+ if (!matchingPatterns.length)
161
+ return {};
162
+ const allPatternsHaveCache = matchingPatterns.every((pattern) => !!_mergedRules[pattern]);
163
+ if (allPatternsHaveCache) {
164
+ return matchingPatterns.reduceRight((result, pattern) => mergeConfigs(result, deepClone(_mergedRules[pattern])), {});
165
+ }
166
+ return matchingPatterns.reduceRight((result, pattern) => mergeConfigs(result, deepClone(_rules[pattern])), {});
167
+ }
168
+ function getParams(url, pattern) {
169
+ return extractParams(url, pattern);
170
+ }
171
+ function match(url) {
172
+ const patterns = getMatchingPatterns(url);
173
+ if (!patterns.length)
174
+ return null;
175
+ const bestPattern = patterns[0];
176
+ return {
177
+ pattern: bestPattern,
178
+ config: deepClone(_rules[bestPattern]),
179
+ params: getParams(url, bestPattern)
180
+ };
181
+ }
182
+ function clear() {
183
+ _rules = {};
184
+ clearMergedRules();
185
+ _rulesModified = false;
186
+ }
187
+ function clearMergedRules() {
188
+ _mergedRules = {};
189
+ _rulesModified = true;
190
+ }
191
+ function precomputeMergedRules(urls) {
192
+ if (_rulesModified) {
193
+ updateMergeRules();
194
+ }
195
+ for (const url of urls) {
196
+ if (!_mergedRules[url]) {
197
+ const patterns = getMatchingPatterns(url);
198
+ if (patterns.length) {
199
+ _mergedRules[url] = computeMergedConfig(url, patterns);
200
+ }
201
+ }
202
+ }
203
+ }
204
+ function setMergedRule(url, config) {
205
+ _mergedRules[url] = deepClone(config);
206
+ if (url.includes("*") || url.includes(":")) {
207
+ _rulesModified = false;
208
+ }
209
+ }
210
+ function getMergedRules() {
211
+ return deepClone(_mergedRules);
212
+ }
213
+ function configure(config) {
214
+ importRules(config);
215
+ }
216
+ function updateMergeRules() {
217
+ if (!_rulesModified) {
218
+ return _mergedRules;
219
+ }
220
+ _mergedRules = {};
221
+ const rulePatterns = Object.keys(_rules);
222
+ for (const pattern of rulePatterns) {
223
+ const patterns = getMatchingPatterns(pattern);
224
+ _mergedRules[pattern] = computeMergedConfig(pattern, patterns);
225
+ }
226
+ for (const pattern of rulePatterns) {
227
+ if (pattern.includes("/:")) {
228
+ const segments = pattern.split("/");
229
+ let partialPath = "";
230
+ for (const segment of segments) {
231
+ if (segment) {
232
+ partialPath += `/${segment}`;
233
+ if (segment.startsWith(":") && partialPath !== pattern) {
234
+ const samplePath = partialPath.replace(/:\w+/g, "sample");
235
+ const patterns = getMatchingPatterns(samplePath);
236
+ _mergedRules[samplePath] = computeMergedConfig(samplePath, patterns);
237
+ }
238
+ }
239
+ }
240
+ }
241
+ }
242
+ _rulesModified = false;
243
+ return _mergedRules;
244
+ }
245
+ return {
246
+ // Public getters that return a cloned version to prevent direct modification
247
+ get rules() {
248
+ return getRules();
249
+ },
250
+ get mergedRules() {
251
+ return getMergedRules();
252
+ },
253
+ // API methods
254
+ importRules,
255
+ exportRules,
256
+ addRule,
257
+ updateRule,
258
+ removeRule,
259
+ matchesRule,
260
+ getMatchingPatterns,
261
+ getConfig,
262
+ getParams,
263
+ match,
264
+ clear,
265
+ clearMergedRules,
266
+ precomputeMergedRules,
267
+ setMergedRule,
268
+ getMergedRules,
269
+ configure,
270
+ updateMergeRules
271
+ };
272
+ }
273
+
274
+ export { createRouteRules as c };
package/dist/cli/dev.mjs CHANGED
@@ -15,6 +15,7 @@ import 'silgi/kit';
15
15
  import 'silgi/runtime/meta';
16
16
  import 'silgi/types';
17
17
  import 'unimport';
18
+ import '../_chunks/routeRules.mjs';
18
19
  import './env.mjs';
19
20
  import '@clack/prompts';
20
21
  import 'dotenv';
@@ -27,6 +27,7 @@ const SilgiCLIDefaults = {
27
27
  version: "0.0.1"
28
28
  }
29
29
  },
30
+ serviceParseModules: [],
30
31
  storages: [],
31
32
  devServer: {
32
33
  watch: []
@@ -56,6 +57,7 @@ const SilgiCLIDefaults = {
56
57
  // Modules
57
58
  _modules: [],
58
59
  modules: [],
60
+ routeRules: {},
59
61
  // Features
60
62
  // experimental: {},
61
63
  future: {},
@@ -92,7 +94,6 @@ const SilgiCLIDefaults = {
92
94
  // handlers: [],
93
95
  // devHandlers: [],
94
96
  // errorHandler: undefined,
95
- // routeRules: {},
96
97
  // Advanced
97
98
  typescript: {
98
99
  strict: false,
@@ -1,17 +1,18 @@
1
1
  import { defineCommand } from 'citty';
2
2
  import consola from 'consola';
3
- import { resolve } from 'pathe';
3
+ import { join, resolve } from 'pathe';
4
4
  import { version } from 'silgi/meta';
5
+ import { writeFile } from 'silgi/kit';
5
6
  import { c as createSilgiCLI, p as prepare$1, w as writeTypesAndFiles, a as writeCoreFile } from './writeTypesAndFiles.mjs';
6
7
  import { c as commonArgs } from './common.mjs';
7
8
  import 'node:fs';
8
9
  import 'node:fs/promises';
9
10
  import 'hookable';
10
11
  import 'silgi/core';
11
- import 'silgi/kit';
12
12
  import 'silgi/runtime/meta';
13
13
  import 'silgi/types';
14
14
  import 'unimport';
15
+ import '../_chunks/routeRules.mjs';
15
16
  import './env.mjs';
16
17
  import '@clack/prompts';
17
18
  import 'dotenv';
@@ -39,6 +40,48 @@ import 'pathe/utils';
39
40
  import 'untyped';
40
41
  import './types.mjs';
41
42
 
43
+ function serializeToString(obj, indent = 0) {
44
+ const spacing = " ".repeat(indent);
45
+ const innerSpacing = " ".repeat(indent + 4);
46
+ if (obj === null || obj === void 0)
47
+ return "null";
48
+ if (typeof obj === "function")
49
+ return obj.toString();
50
+ if (typeof obj !== "object")
51
+ return JSON.stringify(obj);
52
+ if (Array.isArray(obj)) {
53
+ if (obj.length === 0)
54
+ return "[]";
55
+ const items = obj.map((item) => serializeToString(item, indent + 4));
56
+ return `[
57
+ ${innerSpacing}${items.join(`,
58
+ ${innerSpacing}`)}
59
+ ${spacing}]`;
60
+ }
61
+ const entries = Object.entries(obj);
62
+ if (entries.length === 0)
63
+ return "{}";
64
+ const props = entries.map(
65
+ ([key, value]) => `${innerSpacing}"${key}": ${serializeToString(value, indent + 4)}`
66
+ );
67
+ return `{
68
+ ${props.join(",\n")}
69
+ ${spacing}}`;
70
+ }
71
+ async function prepareBuild(silgi) {
72
+ const exportedRules = silgi.routeRules.exportRules();
73
+ const serialized = serializeToString(exportedRules);
74
+ const content = `/* eslint-disable */
75
+ // @ts-nocheck
76
+ // This file is auto-generated at build time
77
+ // Contains route rules with preserved functions
78
+
79
+ export const routeRules = ${serialized}
80
+ `;
81
+ const file = join(silgi.options.silgi.serverDir, "rules.ts");
82
+ await writeFile(file, content);
83
+ }
84
+
42
85
  const prepare = defineCommand({
43
86
  meta: {
44
87
  name: "prepare",
@@ -73,6 +116,7 @@ const prepare = defineCommand({
73
116
  await prepare$1();
74
117
  await writeTypesAndFiles(silgi);
75
118
  await writeCoreFile(silgi);
119
+ await prepareBuild(silgi);
76
120
  const close = async () => {
77
121
  await silgi.close();
78
122
  await silgi.callHook("close", silgi);