crelte 0.5.13 → 0.5.15

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 (56) hide show
  1. package/dist/queries/Queries.d.ts +3 -0
  2. package/dist/queries/Queries.d.ts.map +1 -1
  3. package/dist/queries/Queries.js +3 -0
  4. package/dist/queries/index.d.ts +21 -0
  5. package/dist/queries/index.d.ts.map +1 -1
  6. package/dist/queries/vars.d.ts +25 -2
  7. package/dist/queries/vars.d.ts.map +1 -1
  8. package/dist/queries/vars.js +42 -1
  9. package/dist/routing/route/BaseRoute.d.ts.map +1 -1
  10. package/dist/routing/route/BaseRoute.js +4 -17
  11. package/dist/server/Request.d.ts +2 -2
  12. package/dist/server/Request.js +2 -2
  13. package/dist/server/queries/QueryGqlRoute.d.ts +3 -1
  14. package/dist/server/queries/QueryGqlRoute.d.ts.map +1 -1
  15. package/dist/server/queries/QueryGqlRoute.js +4 -2
  16. package/dist/server/queries/QueryHandleRoute.d.ts +3 -2
  17. package/dist/server/queries/QueryHandleRoute.d.ts.map +1 -1
  18. package/dist/server/queries/QueryHandleRoute.js +4 -2
  19. package/dist/server/queries/queries.d.ts.map +1 -1
  20. package/dist/server/queries/queries.js +12 -2
  21. package/dist/server/queries/routes.d.ts +3 -2
  22. package/dist/server/queries/routes.d.ts.map +1 -1
  23. package/dist/server/queries/routes.js +9 -3
  24. package/dist/static/index.d.ts +1 -1
  25. package/dist/std/index.d.ts +1 -0
  26. package/dist/std/index.d.ts.map +1 -1
  27. package/dist/std/index.js +1 -0
  28. package/dist/std/url/index.d.ts +40 -0
  29. package/dist/std/url/index.d.ts.map +1 -0
  30. package/dist/std/url/index.js +65 -0
  31. package/dist/std/url/utils.d.ts +19 -0
  32. package/dist/std/url/utils.d.ts.map +1 -0
  33. package/dist/std/url/utils.js +40 -0
  34. package/dist/translations/index.d.ts +2 -2
  35. package/dist/translations/index.d.ts.map +1 -1
  36. package/dist/translations/translationsPlugin.d.ts +4 -1
  37. package/dist/translations/translationsPlugin.d.ts.map +1 -1
  38. package/dist/translations/translationsPlugin.js +32 -2
  39. package/dist/vite/index.js +3 -2
  40. package/package.json +5 -1
  41. package/src/queries/Queries.ts +3 -0
  42. package/src/queries/index.ts +25 -0
  43. package/src/queries/vars.ts +58 -3
  44. package/src/routing/route/BaseRoute.ts +8 -22
  45. package/src/server/Request.ts +2 -2
  46. package/src/server/queries/QueryGqlRoute.ts +17 -2
  47. package/src/server/queries/QueryHandleRoute.ts +5 -2
  48. package/src/server/queries/queries.ts +29 -3
  49. package/src/server/queries/routes.ts +17 -3
  50. package/src/static/index.ts +1 -1
  51. package/src/std/index.ts +1 -0
  52. package/src/std/url/index.ts +78 -0
  53. package/src/std/url/utils.ts +48 -0
  54. package/src/translations/index.ts +2 -0
  55. package/src/translations/translationsPlugin.ts +46 -3
  56. package/src/vite/index.ts +4 -2
@@ -4,6 +4,12 @@ import ServerRouter from '../ServerRouter.js';
4
4
 
5
5
  export type CacheIfFn = (response: any, vars: Record<string, any>) => boolean;
6
6
 
7
+ /// Either throw or return a boolean
8
+ export type ValidIfFn = (
9
+ vars: Record<string, any>,
10
+ sr: ServerRouter,
11
+ ) => void | boolean;
12
+
7
13
  /// Anything other than returning undefined will replace the response
8
14
  //
9
15
  // Note that even if you return undefined since the response is by reference
@@ -20,13 +26,14 @@ export type HandleFn = (
20
26
  ) => Promise<any> | any;
21
27
 
22
28
  /**
23
- * Returns the validated variables if some vars where defined
29
+ * Returns the validated variables if some vars were defined
24
30
  * else just returns all vars
25
31
  */
26
32
  export function validateVars(
27
33
  qvars: Record<string, QueryVar> | null,
28
34
  vars: any,
29
- cs: ServerRouter,
35
+ validIfFn: ValidIfFn | null,
36
+ sr: ServerRouter,
30
37
  ): Record<string, any> {
31
38
  if (!vars || typeof vars !== 'object')
32
39
  throw new Error('expected an object as vars');
@@ -36,7 +43,14 @@ export function validateVars(
36
43
  const nVars: Record<string, any> = {};
37
44
 
38
45
  for (const [k, v] of Object.entries(qvars)) {
39
- nVars[k] = v.validValue(vars[k], cs);
46
+ nVars[k] = v.validValue(vars[k], sr);
47
+ }
48
+
49
+ if (validIfFn) {
50
+ // or throw
51
+ const valid = validIfFn(nVars, sr);
52
+ if (typeof valid === 'boolean' && !valid)
53
+ throw new Error('invalid variables for query');
40
54
  }
41
55
 
42
56
  return nVars;
@@ -12,7 +12,7 @@ export type CrelteStaticRequest = CrelteRequest & {
12
12
  *
13
13
  * @example
14
14
  * ```js
15
- * router.add('/blog/:slug', async (csr) => {
15
+ * router.add('/blog/:slug', async csr => {
16
16
  * const blogData = await fetchBlogData(csr.getParam('slug'));
17
17
  *
18
18
  * return {
package/src/std/index.ts CHANGED
@@ -10,6 +10,7 @@
10
10
  * - {@link std/stores}
11
11
  * - {@link std/sync}
12
12
  * - {@link std/rand}
13
+ * - {@link std/url}
13
14
  */
14
15
 
15
16
  /**
@@ -0,0 +1,78 @@
1
+ import { BaseRoute } from '../../routing/index.js';
2
+ import { deleteSearchParam, pathnameEq, searchEq, toUrl } from './utils.js';
3
+
4
+ /**
5
+ * Sets the search params of a URL based on the provided options.
6
+ *
7
+ * If a value is `null`, `undefined`, or an empty string, the corresponding
8
+ * search param will be deleted.
9
+ *
10
+ * #### Example
11
+ * ```js
12
+ * urlWithSearch(entry.url, { p: 1 });
13
+ * // or remove a value
14
+ * urlWithSearch(entry.url, { p: null });
15
+ * ```
16
+ */
17
+ export function urlWithSearch(
18
+ url: BaseRoute | URL | string | null | undefined,
19
+ opts: Record<string, string | number | null | undefined>,
20
+ ): string | null {
21
+ if (!url) return null;
22
+
23
+ url = toUrl(url);
24
+
25
+ for (const [k, v] of Object.entries(opts)) {
26
+ if (!deleteSearchParam(v)) {
27
+ url.searchParams.set(k, v as string);
28
+ } else {
29
+ url.searchParams.delete(k);
30
+ }
31
+ }
32
+
33
+ return url.href;
34
+ }
35
+
36
+ /**
37
+ * Compares two URLs for equality.
38
+ * Normally search and hash are ignored.
39
+ *
40
+ * If either or both url is `null` or `undefined`, the function will return
41
+ * `false`.
42
+ *
43
+ * #### Example
44
+ * ```svelte
45
+ * <script>
46
+ * import { getRoute } from 'crelte';
47
+ *
48
+ * const route = getRoute();
49
+ * </script>
50
+ *
51
+ * <a href={item.url} class:active={urlEq($route, item.url)}>
52
+ * {item.title}
53
+ * </a>
54
+ * ```
55
+ */
56
+ export function urlEq(
57
+ a: BaseRoute | URL | string | null | undefined,
58
+ b: BaseRoute | URL | string | null | undefined,
59
+ opts?: { search?: boolean; hash?: boolean },
60
+ ): boolean {
61
+ if (!a || !b) return false;
62
+
63
+ a = toUrl(a);
64
+ b = toUrl(b);
65
+
66
+ // check origin and pathname
67
+ const baseMatches =
68
+ a.origin === b.origin && pathnameEq(a.pathname, b.pathname);
69
+ if (!baseMatches) return false;
70
+
71
+ // check search
72
+ if (opts?.search && !searchEq(a.searchParams, b.searchParams)) return false;
73
+
74
+ // check hash
75
+ if (opts?.hash && a.hash !== b.hash) return false;
76
+
77
+ return true;
78
+ }
@@ -0,0 +1,48 @@
1
+ import { BaseRoute } from '../../routing/index.js';
2
+
3
+ /**
4
+ * Checks if a search param should be removed.
5
+ * This is the case if the value is `null`, `undefined`, or an empty string.
6
+ */
7
+ export function deleteSearchParam(value: string | number | null | undefined) {
8
+ return (
9
+ typeof value === 'undefined' ||
10
+ value === null ||
11
+ (typeof value === 'string' && value === '')
12
+ );
13
+ }
14
+
15
+ /**
16
+ * Converts a `BaseRoute`, `URL`, or string to a `URL` object.
17
+ */
18
+ export function toUrl(url: BaseRoute | URL | string): URL {
19
+ if (typeof url === 'string') return new URL(url);
20
+
21
+ if (url instanceof BaseRoute) return new URL(url.url);
22
+
23
+ return url;
24
+ }
25
+
26
+ /**
27
+ * Compares two `URLSearchParams` objects for equality.
28
+ */
29
+ export function searchEq(a: URLSearchParams, b: URLSearchParams): boolean {
30
+ if (a.size !== b.size) return false;
31
+
32
+ // Clone to avoid mutating the original objects
33
+ const cloneA = new URLSearchParams(a);
34
+ const cloneB = new URLSearchParams(b);
35
+
36
+ cloneA.sort();
37
+ cloneB.sort();
38
+
39
+ return cloneA.toString() === cloneB.toString();
40
+ }
41
+
42
+ /**
43
+ * Compares two pathnames for equality, ignoring trailing slashes.
44
+ */
45
+ export function pathnameEq(a: string, b: string): boolean {
46
+ // check for trailing slashes
47
+ return a === b || a === b + '/' || a + '/' === b;
48
+ }
@@ -13,6 +13,7 @@ import {
13
13
  createTranslations,
14
14
  getTranslationsPlugin,
15
15
  type TranslateFunction,
16
+ type TranslationVarValue,
16
17
  type TranslateStore,
17
18
  type Translations,
18
19
  TranslationsPlugin,
@@ -50,6 +51,7 @@ export {
50
51
  LoaderCreator,
51
52
  TranslateStore,
52
53
  TranslateFunction,
54
+ TranslationVarValue,
53
55
  // loaders
54
56
  createFileLoader,
55
57
  createGlobalLoader,
@@ -7,8 +7,9 @@ import { getCrelte } from '../index.js';
7
7
 
8
8
  export type TranslateFunction = (
9
9
  key: string,
10
- // replacements?: Record<string, string>,
10
+ vars?: Record<string, TranslationVarValue>,
11
11
  ) => string;
12
+ export type TranslationVarValue = string | number;
12
13
  export type TranslateStore = Readable<TranslateFunction>;
13
14
  export type Translations = Record<string, string>;
14
15
  export type TranslationsPluginOptions = {
@@ -89,18 +90,60 @@ export class TranslationsPlugin implements Plugin {
89
90
  /** @hidden */
90
91
  z_createTranslateStore(namespace: string): TranslateStore {
91
92
  const store = derived(this.crelte.router.site, site => {
92
- return (key: string) => {
93
+ return (
94
+ key: string,
95
+ vars?: Record<string, TranslationVarValue>,
96
+ ) => {
93
97
  const lang = site?.language ?? this.firstLang;
94
98
  if (!lang) throw new Error('no lang');
95
99
 
96
100
  const data = this.get(lang, namespace);
97
101
  if (!data) console.error(`namespace '${namespace}' not loaded`);
98
- return data?.[key] || key;
102
+
103
+ const text = data?.[key] || key;
104
+ if (!vars) return text;
105
+ return this.z_resolveVariables(text, vars);
99
106
  };
100
107
  });
101
108
 
102
109
  return new Readable(store);
103
110
  }
111
+
112
+ /** @hidden */
113
+ z_resolveVariables(
114
+ text: string,
115
+ vars: Record<string, TranslationVarValue> = {},
116
+ ): string {
117
+ let result = '';
118
+ let index = 0;
119
+
120
+ while (index < text.length) {
121
+ const openIndex = text.indexOf('{', index);
122
+ if (openIndex === -1) {
123
+ result += text.slice(index);
124
+ break;
125
+ }
126
+
127
+ const closeIndex = text.indexOf('}', openIndex + 1);
128
+ if (closeIndex === -1) {
129
+ result += text.slice(index);
130
+ break;
131
+ }
132
+
133
+ result += text.slice(index, openIndex);
134
+
135
+ const key = text.slice(openIndex + 1, closeIndex);
136
+ if (key in vars) {
137
+ result += vars[key];
138
+ } else {
139
+ result += `{${key}}`;
140
+ }
141
+
142
+ index = closeIndex + 1;
143
+ }
144
+
145
+ return result;
146
+ }
104
147
  }
105
148
 
106
149
  /**
package/src/vite/index.ts CHANGED
@@ -180,7 +180,7 @@ export default function crelte(opts?: CrelteOptions): Plugin {
180
180
  publicDir: isSsrBuild ? false : 'public',
181
181
  base: '/',
182
182
  server: {
183
- port: 8080,
183
+ port: config.server?.port ?? 8080,
184
184
  },
185
185
  optimizeDeps: {
186
186
  exclude: ['crelte'],
@@ -338,7 +338,9 @@ async function serveVite(env: EnvData, vite: ViteDevServer) {
338
338
  nReq: Connect.IncomingMessage,
339
339
  res: ServerResponse,
340
340
  ) => {
341
- const protocol = vite.config.server.https ? 'https' : 'http';
341
+ const protocol =
342
+ (nReq.headers['x-forwarded-proto'] as string | undefined) ??
343
+ (vite.config.server.https ? 'https' : 'http');
342
344
  const baseUrl = protocol + '://' + nReq.headers['host'];
343
345
 
344
346
  const req = requestToWebRequest(baseUrl, nReq);