@turnipxenon/pineapple 4.5.0-alpha.2 → 4.5.0-alpha.4

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.
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Server middleware that handles locale-based routing and request processing.
3
+ *
4
+ * This middleware performs several key functions:
5
+ *
6
+ * 1. Determines the locale for the incoming request using configured strategies
7
+ * 2. Handles URL localization and redirects
8
+ * 3. Maintains locale state using AsyncLocalStorage to prevent request interference
9
+ *
10
+ * When URL strategy is used:
11
+ *
12
+ * - If URL doesn't match the determined locale, redirects to localized URL
13
+ * - De-localizes URLs before passing to server (e.g., `/fr/about` → `/about`)
14
+ *
15
+ * @template T - The return type of the resolve function
16
+ *
17
+ * @param {Request} request - The incoming request object
18
+ * @param {(args: { request: Request, locale: import("./runtime.js").Locale }) => T | Promise<T>} resolve - Function to handle the request
19
+ *
20
+ * @returns {Promise<Response>}
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * // Basic usage in metaframeworks like NextJS, SvelteKit, Astro, Nuxt, etc.
25
+ * export const handle = async ({ event, resolve }) => {
26
+ * return serverMiddleware(event.request, ({ request, locale }) => {
27
+ * // let the framework further resolve the request
28
+ * return resolve(request);
29
+ * });
30
+ * };
31
+ * ```
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * // Usage in a framework like Express JS or Hono
36
+ * app.use(async (req, res, next) => {
37
+ * const result = await serverMiddleware(req, ({ request, locale }) => {
38
+ * // If a redirect happens this won't be called
39
+ * return next(request);
40
+ * });
41
+ * });
42
+ * ```
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * // Usage in serverless environments like Cloudflare Workers
47
+ * // ⚠️ WARNING: This should ONLY be used in serverless environments like Cloudflare Workers.
48
+ * // Disabling AsyncLocalStorage in traditional server environments risks cross-request pollution where state from
49
+ * // one request could leak into another concurrent request.
50
+ * export default {
51
+ * fetch: async (request) => {
52
+ * return serverMiddleware(
53
+ * request,
54
+ * ({ request, locale }) => handleRequest(request, locale),
55
+ * { disableAsyncLocalStorage: true }
56
+ * );
57
+ * }
58
+ * };
59
+ * ```
60
+ */
61
+ export function paraglideMiddleware<T>(request: Request, resolve: (args: {
62
+ request: Request;
63
+ locale: import("./runtime.js").Locale;
64
+ }) => T | Promise<T>): Promise<Response>;
@@ -0,0 +1,161 @@
1
+ // eslint-disable
2
+
3
+ import * as runtime from "./runtime.js";
4
+
5
+ /**
6
+ * Server middleware that handles locale-based routing and request processing.
7
+ *
8
+ * This middleware performs several key functions:
9
+ *
10
+ * 1. Determines the locale for the incoming request using configured strategies
11
+ * 2. Handles URL localization and redirects
12
+ * 3. Maintains locale state using AsyncLocalStorage to prevent request interference
13
+ *
14
+ * When URL strategy is used:
15
+ *
16
+ * - If URL doesn't match the determined locale, redirects to localized URL
17
+ * - De-localizes URLs before passing to server (e.g., `/fr/about` → `/about`)
18
+ *
19
+ * @template T - The return type of the resolve function
20
+ *
21
+ * @param {Request} request - The incoming request object
22
+ * @param {(args: { request: Request, locale: import("./runtime.js").Locale }) => T | Promise<T>} resolve - Function to handle the request
23
+ *
24
+ * @returns {Promise<Response>}
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * // Basic usage in metaframeworks like NextJS, SvelteKit, Astro, Nuxt, etc.
29
+ * export const handle = async ({ event, resolve }) => {
30
+ * return serverMiddleware(event.request, ({ request, locale }) => {
31
+ * // let the framework further resolve the request
32
+ * return resolve(request);
33
+ * });
34
+ * };
35
+ * ```
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * // Usage in a framework like Express JS or Hono
40
+ * app.use(async (req, res, next) => {
41
+ * const result = await serverMiddleware(req, ({ request, locale }) => {
42
+ * // If a redirect happens this won't be called
43
+ * return next(request);
44
+ * });
45
+ * });
46
+ * ```
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * // Usage in serverless environments like Cloudflare Workers
51
+ * // ⚠️ WARNING: This should ONLY be used in serverless environments like Cloudflare Workers.
52
+ * // Disabling AsyncLocalStorage in traditional server environments risks cross-request pollution where state from
53
+ * // one request could leak into another concurrent request.
54
+ * export default {
55
+ * fetch: async (request) => {
56
+ * return serverMiddleware(
57
+ * request,
58
+ * ({ request, locale }) => handleRequest(request, locale),
59
+ * { disableAsyncLocalStorage: true }
60
+ * );
61
+ * }
62
+ * };
63
+ * ```
64
+ */
65
+ export async function paraglideMiddleware(request, resolve) {
66
+ if (!runtime.disableAsyncLocalStorage && !runtime.serverAsyncLocalStorage) {
67
+ const { AsyncLocalStorage } = await import("async_hooks");
68
+ runtime.overwriteServerAsyncLocalStorage(new AsyncLocalStorage());
69
+ }
70
+ else if (!runtime.serverAsyncLocalStorage) {
71
+ runtime.overwriteServerAsyncLocalStorage(createMockAsyncLocalStorage());
72
+ }
73
+ const locale = runtime.extractLocaleFromRequest(request);
74
+ const origin = new URL(request.url).origin;
75
+ // if the client makes a request to a URL that doesn't match
76
+ // the localizedUrl, redirect the client to the localized URL
77
+ if (request.headers.get("Sec-Fetch-Dest") === "document" &&
78
+ runtime.strategy.includes("url")) {
79
+ const localizedUrl = runtime.localizeUrl(request.url, { locale });
80
+ if (localizedUrl.href !== request.url) {
81
+ return Response.redirect(localizedUrl, 307);
82
+ }
83
+ }
84
+ // If the strategy includes "url", we need to de-localize the URL
85
+ // before passing it to the server middleware.
86
+ //
87
+ // The middleware is responsible for mapping a localized URL to the
88
+ // de-localized URL e.g. `/en/about` to `/about`. Otherwise,
89
+ // the server can't render the correct page.
90
+ const newRequest = runtime.strategy.includes("url")
91
+ ? new Request(runtime.deLocalizeUrl(request.url), request)
92
+ : // need to create a new request object because some metaframeworks (nextjs!) throw otherwise
93
+ // https://github.com/opral/inlang-paraglide-js/issues/411
94
+ new Request(request);
95
+ // the message functions that have been called in this request
96
+ /** @type {Set<string>} */
97
+ const messageCalls = new Set();
98
+ const response = await runtime.serverAsyncLocalStorage?.run({ locale, origin, messageCalls }, () => resolve({ locale, request: newRequest }));
99
+ // Only modify HTML responses
100
+ if (runtime.experimentalMiddlewareLocaleSplitting &&
101
+ response.headers.get("Content-Type")?.includes("html")) {
102
+ const body = await response.text();
103
+ const messages = [];
104
+ // using .values() to avoid polyfilling in older projects. else the following error is thrown
105
+ // Type 'Set<string>' can only be iterated through when using the '--downlevelIteration' flag or with a '--target' of 'es2015' or higher.
106
+ for (const messageCall of Array.from(messageCalls)) {
107
+ const [id, locale] =
108
+ /** @type {[string, import("./runtime.js").Locale]} */ (messageCall.split(":"));
109
+ messages.push(`${id}: ${compiledBundles[id]?.[locale]}`);
110
+ }
111
+ const script = `<script>globalThis.__paraglide_ssr = { ${messages.join(",")} }</script>`;
112
+ // Insert the script before the closing head tag
113
+ const newBody = body.replace("</head>", `${script}</head>`);
114
+ // Create a new response with the modified body
115
+ // Clone all headers except Content-Length which will be set automatically
116
+ const newHeaders = new Headers(response.headers);
117
+ newHeaders.delete("Content-Length"); // Let the browser calculate the correct length
118
+ return new Response(newBody, {
119
+ status: response.status,
120
+ statusText: response.statusText,
121
+ headers: newHeaders,
122
+ });
123
+ }
124
+ return response;
125
+ }
126
+ /**
127
+ * Creates a mock AsyncLocalStorage implementation for environments where
128
+ * native AsyncLocalStorage is not available or disabled.
129
+ *
130
+ * This mock implementation mimics the behavior of the native AsyncLocalStorage
131
+ * but doesn't require the async_hooks module. It's designed to be used in
132
+ * environments like Cloudflare Workers where AsyncLocalStorage is not available.
133
+ *
134
+ * @returns {import("./runtime.js").ParaglideAsyncLocalStorage}
135
+ */
136
+ function createMockAsyncLocalStorage() {
137
+ /** @type {any} */
138
+ let currentStore = undefined;
139
+ return {
140
+ getStore() {
141
+ return currentStore;
142
+ },
143
+ async run(store, callback) {
144
+ currentStore = store;
145
+ try {
146
+ return await callback();
147
+ }
148
+ finally {
149
+ currentStore = undefined;
150
+ }
151
+ },
152
+ };
153
+ }
154
+ /**
155
+ * The compiled messages for the server middleware.
156
+ *
157
+ * Only populated if `enableMiddlewareOptimizations` is set to `true`.
158
+ *
159
+ * @type {Record<string, Record<import("./runtime.js").Locale, string>>}
160
+ */
161
+ const compiledBundles = {};
@@ -1,2 +1,4 @@
1
- export const getCmsBaseUrl = () => import.meta.env.VITE_CMS_BASE_URL ?? 'https://gitlab.com/turnipxenon-personal/test-obdisian/-/raw/main';
2
- export const getWebBaseUrl = () => import.meta.env.VITE_WEB_BASE_URL ?? '/pineapple';
1
+ // import.meta.env.* works default everywhere
2
+ // env.* is for cloudflare
3
+ export const getCmsBaseUrl = () => import.meta.env.VITE_CMS_BASE_URL ?? env?.VITE_CMS_BASE_URL ?? 'https://gitlab.com/turnipxenon-personal/test-obdisian/-/raw/main';
4
+ export const getWebBaseUrl = () => import.meta.env.VITE_WEB_BASE_URL ?? env?.VITE_WEB_BASE_URL ?? '/pineapple';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@turnipxenon/pineapple",
3
3
  "description": "personal package for base styling for other personal projects",
4
- "version": "4.5.0-alpha.2",
4
+ "version": "4.5.0-alpha.4",
5
5
  "devDependencies": {
6
6
  "@commitlint/cli": "^19.8.0",
7
7
  "@commitlint/config-conventional": "^19.8.0",