@sveltejs/kit 2.61.1 → 2.63.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.
Files changed (53) hide show
  1. package/package.json +14 -3
  2. package/src/cli.js +32 -6
  3. package/src/core/adapt/builder.js +31 -5
  4. package/src/core/adapt/index.js +5 -2
  5. package/src/core/config/index.js +100 -36
  6. package/src/core/config/options.js +1 -0
  7. package/src/core/env.js +313 -8
  8. package/src/core/postbuild/analyse.js +2 -1
  9. package/src/core/postbuild/fallback.js +7 -8
  10. package/src/core/postbuild/prerender.js +6 -2
  11. package/src/core/sync/sync.js +16 -0
  12. package/src/core/sync/write_ambient.js +12 -6
  13. package/src/core/sync/write_env.js +32 -0
  14. package/src/core/sync/write_root.js +1 -1
  15. package/src/core/sync/write_server.js +3 -2
  16. package/src/core/sync/write_tsconfig.js +1 -0
  17. package/src/exports/hooks/index.js +13 -0
  18. package/src/exports/internal/env.js +71 -0
  19. package/src/exports/internal/types.d.ts +3 -0
  20. package/src/exports/node/index.js +2 -10
  21. package/src/exports/public.d.ts +41 -1
  22. package/src/exports/vite/build/build_service_worker.js +20 -4
  23. package/src/exports/vite/dev/index.js +2 -2
  24. package/src/exports/vite/index.js +157 -33
  25. package/src/exports/vite/module_ids.js +10 -1
  26. package/src/exports/vite/utils.js +6 -0
  27. package/src/runtime/app/env/index.js +2 -0
  28. package/src/runtime/app/env/internal.js +11 -0
  29. package/src/runtime/app/env/private.js +1 -0
  30. package/src/runtime/app/env/public/client.js +7 -0
  31. package/src/runtime/app/env/public/index.js +1 -0
  32. package/src/runtime/app/env/public/server.js +7 -0
  33. package/src/runtime/app/env/standard-schema.d.ts +0 -0
  34. package/src/runtime/app/env/types.d.ts +19 -0
  35. package/src/runtime/app/environment/index.js +10 -2
  36. package/src/runtime/app/server/remote/query.js +1 -1
  37. package/src/runtime/app/state/client.js +10 -6
  38. package/src/runtime/client/client.js +68 -51
  39. package/src/runtime/client/remote-functions/prerender.svelte.js +1 -1
  40. package/src/runtime/client/utils.js +1 -1
  41. package/src/runtime/server/env_module.js +13 -3
  42. package/src/runtime/server/fetch.js +12 -14
  43. package/src/runtime/server/index.js +2 -0
  44. package/src/runtime/server/page/render.js +11 -3
  45. package/src/runtime/server/respond.js +8 -11
  46. package/src/types/ambient-private.d.ts +25 -9
  47. package/src/types/global-private.d.ts +2 -0
  48. package/src/types/internal.d.ts +1 -0
  49. package/src/utils/http.js +21 -0
  50. package/src/utils/import.js +2 -2
  51. package/src/version.js +1 -1
  52. package/types/index.d.ts +76 -4
  53. package/types/index.d.ts.map +7 -1
package/src/core/env.js CHANGED
@@ -1,19 +1,126 @@
1
+ /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
2
+ /** @import { EnvVarConfig } from '@sveltejs/kit' */
3
+ /** @import { ValidatedKitConfig } from 'types' */
4
+ import path from 'node:path';
5
+ import process from 'node:process';
6
+ import * as vite from 'vite';
7
+ import * as devalue from 'devalue';
1
8
  import { GENERATED_COMMENT } from '../constants.js';
2
9
  import { dedent } from './sync/utils.js';
3
- import { runtime_base } from './utils.js';
10
+ import { runtime_base, runtime_directory } from './utils.js';
11
+ import { resolve_entry } from '../utils/filesystem.js';
12
+ import { handle_issues, validate } from '../exports/internal/env.js';
13
+ import { get_config_aliases } from '../exports/vite/utils.js';
4
14
 
5
15
  /**
6
16
  * @typedef {'public' | 'private'} EnvType
7
17
  */
8
18
 
19
+ let warned = false;
20
+
21
+ /**
22
+ * @param {import('types').ValidatedKitConfig} config
23
+ * @returns {string | null}
24
+ */
25
+ export function resolve_explicit_env_entry(config) {
26
+ const resolved = resolve_entry(path.join(config.files.src, 'env'));
27
+
28
+ if (resolved) {
29
+ if (config.experimental.explicitEnvironmentVariables) {
30
+ return resolved;
31
+ }
32
+
33
+ if (!warned) {
34
+ console.warn(
35
+ `${path.relative(process.cwd(), resolved)} requires the \`experimental.explicitEnvironmentVariables\` flag to be set`
36
+ );
37
+ warned = true;
38
+ }
39
+ } else if (config.experimental.explicitEnvironmentVariables) {
40
+ console.warn(
41
+ 'experimental.explicitEnvironmentVariables was set, but no src/env.ts or src/env.js file could be found'
42
+ );
43
+ }
44
+
45
+ return null;
46
+ }
47
+
48
+ /**
49
+ * @param {ValidatedKitConfig} kit
50
+ * @param {string | null} file
51
+ * @param {string} mode
52
+ * @returns {Promise<Record<string, EnvVarConfig<any>> | null>}
53
+ */
54
+ export async function load_explicit_env(kit, file, mode) {
55
+ if (!file) return null;
56
+
57
+ const server = await vite.createServer({
58
+ configFile: false,
59
+ logLevel: 'silent',
60
+ mode,
61
+ define: {
62
+ __SVELTEKIT_APP_VERSION__: JSON.stringify(kit.version.name) // needed by $app/env
63
+ },
64
+ resolve: {
65
+ alias: [
66
+ { find: '$app/env', replacement: `${runtime_directory}/app/env` },
67
+ ...get_config_aliases(kit)
68
+ ]
69
+ }
70
+ });
71
+
72
+ /** @type {Record<string, EnvVarConfig<any>>} */
73
+ let variables;
74
+
75
+ try {
76
+ ({ variables } = await server.ssrLoadModule(file));
77
+
78
+ if (!variables || typeof variables !== 'object') {
79
+ throw new Error(`${file} must export a variables object`);
80
+ }
81
+
82
+ // validate
83
+ for (const name of Object.keys(variables)) {
84
+ if (!valid_identifier.test(name) || reserved.has(name)) {
85
+ throw new Error(`Invalid environment variable name ${JSON.stringify(name)}`);
86
+ }
87
+ }
88
+ } catch (e) {
89
+ const error = /** @type {any} */ (e || {});
90
+
91
+ if (
92
+ error.code === 'ERR_MODULE_NOT_FOUND' &&
93
+ error.message?.includes(`Cannot find module '$app`)
94
+ ) {
95
+ throw new Error(
96
+ `Cannot import \`$app/*\` modules other than \`$app/env\` inside \`src/env\``,
97
+ { cause: e }
98
+ );
99
+ }
100
+
101
+ throw error;
102
+ } finally {
103
+ await server.close();
104
+ }
105
+
106
+ return variables;
107
+ }
108
+
9
109
  /**
10
110
  * @param {string} id
11
111
  * @param {Record<string, string>} env
112
+ * @param {boolean} disabled
12
113
  * @returns {string}
13
114
  */
14
- export function create_static_module(id, env) {
115
+ export function create_static_module(id, env, disabled) {
15
116
  /** @type {string[]} */
16
- const declarations = [];
117
+ const statements = [];
118
+
119
+ if (disabled) {
120
+ statements.push(
121
+ `throw new Error('Cannot import \`${id}\` when \`experimental.explicitEnvironmentVariables\` is enabled. Use \`${id.replace('$env/static', '$app/env')}\` instead.');`
122
+ );
123
+ }
17
124
 
18
125
  for (const key in env) {
19
126
  if (!valid_identifier.test(key) || reserved.has(key)) {
@@ -23,24 +130,199 @@ export function create_static_module(id, env) {
23
130
  const comment = `/** @type {import('${id}').${key}} */`;
24
131
  const declaration = `export const ${key} = ${JSON.stringify(env[key])};`;
25
132
 
26
- declarations.push(`${comment}\n${declaration}`);
133
+ statements.push(`${comment}\n${declaration}`);
27
134
  }
28
135
 
29
- return GENERATED_COMMENT + declarations.join('\n\n');
136
+ return GENERATED_COMMENT + statements.join('\n\n');
30
137
  }
31
138
 
32
139
  /**
33
140
  * @param {EnvType} type
34
141
  * @param {Record<string, string> | undefined} dev_values If in a development mode, values to pre-populate the module with.
142
+ * @param {boolean} disabled
35
143
  */
36
- export function create_dynamic_module(type, dev_values) {
144
+ export function create_dynamic_module(type, dev_values, disabled) {
145
+ const prelude = disabled
146
+ ? `throw new Error('Cannot import \`$env/dynamic/${type}\` when \`experimental.explicitEnvironmentVariables\` is enabled. Use \`$app/env/${type}\` instead.');\n\n`
147
+ : '';
148
+
37
149
  if (dev_values) {
38
150
  const keys = Object.entries(dev_values).map(
39
151
  ([k, v]) => `${JSON.stringify(k)}: ${JSON.stringify(v)}`
40
152
  );
41
- return `export const env = {\n${keys.join(',\n')}\n}`;
153
+ return `${prelude}export const env = {\n${keys.join(',\n')}\n}`;
154
+ }
155
+ return `${prelude}export { ${type}_env as env } from '${runtime_base}/shared-server.js';`;
156
+ }
157
+
158
+ /**
159
+ * Creates the `__sveltekit/env` module
160
+ * @param {Record<string, EnvVarConfig<any>> | null} variables
161
+ * @param {Record<string, string>} env
162
+ * @param {string | null} entry
163
+ */
164
+ export function create_sveltekit_env(variables, env, entry) {
165
+ const imports = entry
166
+ ? [
167
+ `import { variables } from ${JSON.stringify(entry)};`,
168
+ `import { validate, handle_issues } from '@sveltejs/kit/internal/env';`
169
+ ]
170
+ : [`const variables = {};`, `const handle_issues = () => {};`];
171
+
172
+ const declarations = [];
173
+ const setters = [];
174
+
175
+ /** @type {Record<string, StandardSchemaV1.Issue[]>} */
176
+ const issues = {};
177
+
178
+ for (const [name, config] of Object.entries(variables ?? {})) {
179
+ if (config.static) {
180
+ if (config.public) {
181
+ const value = validate(variables ?? {}, env[name], name, issues);
182
+ declarations.push(`explicit_public_env.${name} = ${devalue.uneval(value)};`);
183
+ }
184
+ } else {
185
+ setters.push(
186
+ `const ${name} = validate(variables, env.${name}, ${JSON.stringify(name)}, issues);`
187
+ );
188
+
189
+ if (config.public) {
190
+ setters.push(`explicit_public_env.${name} = ${name};`);
191
+ setters.push(`rendered_env.${name} = ${name};`);
192
+ } else {
193
+ setters.push(`dynamic_private_env.${name} = ${name};`);
194
+ }
195
+ }
196
+ }
197
+
198
+ handle_issues(issues);
199
+
200
+ const blocks = [
201
+ GENERATED_COMMENT,
202
+ imports.join('\n'),
203
+ `const issues = {};`,
204
+ 'export { variables }',
205
+ 'export const dynamic_private_env = {};',
206
+ 'export const explicit_public_env = {};',
207
+ 'export const rendered_env = {};',
208
+ ...declarations,
209
+ `handle_issues(issues);`,
210
+ dedent`
211
+ export function set_env(env) {
212
+ const issues = {};
213
+ ${setters.join('\n')}
214
+ handle_issues(issues);
215
+ }`
216
+ ];
217
+
218
+ const module = blocks.join('\n\n');
219
+
220
+ return module;
221
+ }
222
+
223
+ /**
224
+ * Creates the `__sveltekit/env/private` module
225
+ * @param {Record<string, EnvVarConfig<any>> | null} variables
226
+ * @param {Record<string, string>} env
227
+ */
228
+ export function create_sveltekit_env_private(variables, env) {
229
+ if (!variables) {
230
+ return '';
231
+ }
232
+
233
+ /** @type {Record<string, StandardSchemaV1.Issue[]>} */
234
+ const issues = {};
235
+
236
+ /** @type {string[]} */
237
+ const exports = [];
238
+
239
+ for (const [name, config] of Object.entries(variables)) {
240
+ if (config.public) continue;
241
+
242
+ const value = config.static
243
+ ? devalue.uneval(validate(variables, env[name], name, issues))
244
+ : `env.${name}`;
245
+
246
+ exports.push(`export const ${name} = ${value};\n`);
247
+ }
248
+
249
+ handle_issues(issues);
250
+
251
+ return `import { dynamic_private_env as env } from '__sveltekit/env';\n\n${exports.join('')}`;
252
+ }
253
+
254
+ /**
255
+ * Creates the `__sveltekit/env/public/*` modules
256
+ * @param {Record<string, EnvVarConfig<any>> | null} variables
257
+ * @param {Record<string, string>} env
258
+ * @param {string} prelude
259
+ */
260
+ export function create_sveltekit_env_public(variables, env, prelude) {
261
+ if (!variables) {
262
+ return '';
263
+ }
264
+
265
+ /** @type {Record<string, StandardSchemaV1.Issue[]>} */
266
+ const issues = {};
267
+
268
+ /** @type {string[]} */
269
+ const exports = [];
270
+
271
+ for (const [name, config] of Object.entries(variables)) {
272
+ if (!config.public) continue;
273
+
274
+ const value = config.static
275
+ ? devalue.uneval(validate(variables, env[name], name, issues))
276
+ : `env.${name}`;
277
+
278
+ exports.push(`export const ${name} = ${value};\n`);
42
279
  }
43
- return `export { ${type}_env as env } from '${runtime_base}/shared-server.js';`;
280
+
281
+ handle_issues(issues);
282
+
283
+ return `${prelude}\n\n${exports.join('')}`;
284
+ }
285
+
286
+ /**
287
+ * Creates the `__sveltekit/env/service-worker` module used in development
288
+ * (but not in prod, which goes through build_service_worker instead)
289
+ * @param {Record<string, EnvVarConfig<any>> | null} variables
290
+ * @param {Record<string, string>} env
291
+ * @param {string} global
292
+ */
293
+ export function create_sveltekit_env_service_worker_dev(variables, env, global) {
294
+ /** @type {string[]} */
295
+ const properties = [];
296
+
297
+ /** @type {Record<string, StandardSchemaV1.Issue[]>} */
298
+ const issues = {};
299
+
300
+ for (const [name, config] of Object.entries(variables ?? {})) {
301
+ if (!config.public) continue;
302
+
303
+ const value = validate(variables ?? {}, env[name], name, issues);
304
+ properties.push(`${name}: ${devalue.uneval(value)}`);
305
+ }
306
+
307
+ handle_issues(issues);
308
+
309
+ return dedent`
310
+ globalThis.__SVELTEKIT_EXPERIMENTAL_EXPLICIT_ENVIRONMENT_VARIABLES__ = true;
311
+
312
+ ${global} = {
313
+ env: {
314
+ ${properties.join(',\n\t\t') || '// empty'}
315
+ }
316
+ };
317
+ `;
318
+ }
319
+
320
+ /** @param {string} description */
321
+ function create_jsdoc(description) {
322
+ return `/**\n${description
323
+ .split('\n')
324
+ .map((line) => ` * ${line.replaceAll('*/', '*\\/')}`)
325
+ .join('\n')}\n */`;
44
326
  }
45
327
 
46
328
  /**
@@ -98,6 +380,29 @@ export function create_dynamic_types(id, env, { public_prefix, private_prefix })
98
380
  `;
99
381
  }
100
382
 
383
+ /**
384
+ * @param {Record<string, EnvVarConfig<any>>} variables
385
+ * @param {string} relative
386
+ * @param {EnvType} type
387
+ */
388
+ export function create_explicit_env_types(variables, relative, type) {
389
+ const declarations = Object.entries(variables)
390
+ .filter(([_, config]) => !!config.public === (type === 'public'))
391
+ .map(([name, config]) => {
392
+ const comment = config.description ? `${create_jsdoc(config.description)}\n` : '';
393
+ const type = config.schema
394
+ ? `import('@sveltejs/kit/internal/types').StandardSchemaV1.InferOutput<typeof import('${relative}').variables.${name}.schema>`
395
+ : 'string';
396
+ return `${comment}export const ${name}: ${type};`;
397
+ });
398
+
399
+ return dedent`
400
+ declare module '$app/env/${type}' {
401
+ ${declarations.join('\n') || `// no ${type} environment variables were defined`}
402
+ }
403
+ `;
404
+ }
405
+
101
406
  export const reserved = new Set([
102
407
  'do',
103
408
  'if',
@@ -50,7 +50,7 @@ async function analyse({
50
50
 
51
51
  installPolyfills();
52
52
 
53
- // configure `import { building } from '$app/environment'` —
53
+ // configure `import { building } from '$app/environment'` and `$app/env`
54
54
  // essential we do this before analysing the code
55
55
  internal.set_building();
56
56
 
@@ -60,6 +60,7 @@ async function analyse({
60
60
  const public_env = filter_env(env, public_prefix, private_prefix);
61
61
  internal.set_private_env(private_env);
62
62
  internal.set_public_env(public_env);
63
+ internal.set_env(env);
63
64
  internal.set_manifest(manifest);
64
65
  internal.set_read_implementation((file) => createReadableStream(`${server_root}/server/${file}`));
65
66
 
@@ -2,7 +2,6 @@ import { readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { pathToFileURL } from 'node:url';
4
4
  import { installPolyfills } from '../../exports/node/polyfills.js';
5
- import { load_config } from '../config/index.js';
6
5
  import { forked } from '../../utils/fork.js';
7
6
 
8
7
  export default forked(import.meta.url, generate_fallback);
@@ -11,15 +10,15 @@ export default forked(import.meta.url, generate_fallback);
11
10
  * @param {{
12
11
  * manifest_path: string;
13
12
  * env: Record<string, string>
13
+ * out_dir: string;
14
+ * origin: string;
15
+ * assets: string;
14
16
  * }} opts
15
17
  */
16
- async function generate_fallback({ manifest_path, env }) {
17
- /** @type {import('types').ValidatedKitConfig} */
18
- const config = (await load_config()).kit;
19
-
18
+ async function generate_fallback({ manifest_path, env, out_dir, origin, assets }) {
20
19
  installPolyfills();
21
20
 
22
- const server_root = join(config.outDir, 'output');
21
+ const server_root = join(out_dir, 'output');
23
22
 
24
23
  /** @type {import('types').ServerInternalModule} */
25
24
  const { set_building } = await import(pathToFileURL(`${server_root}/server/internal.js`).href);
@@ -35,7 +34,7 @@ async function generate_fallback({ manifest_path, env }) {
35
34
  const server = new Server(manifest);
36
35
  await server.init({ env });
37
36
 
38
- const response = await server.respond(new Request(config.prerender.origin + '/[fallback]'), {
37
+ const response = await server.respond(new Request(origin + '/[fallback]'), {
39
38
  getClientAddress: () => {
40
39
  throw new Error('Cannot read clientAddress during prerendering');
41
40
  },
@@ -44,7 +43,7 @@ async function generate_fallback({ manifest_path, env }) {
44
43
  dependencies: new Map(),
45
44
  remote_responses: new Map()
46
45
  },
47
- read: (file) => readFileSync(join(config.files.assets, file))
46
+ read: (file) => readFileSync(join(assets, file))
48
47
  });
49
48
 
50
49
  if (response.ok) {
@@ -46,7 +46,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) {
46
46
  /** @type {import('types').ServerModule} */
47
47
  const { Server } = await import(pathToFileURL(`${out}/server/index.js`).href);
48
48
 
49
- // configure `import { building } from '$app/environment'` —
49
+ // configure `import { building } from '$app/environment'` and `$app/env`
50
50
  // essential we do this before analysing the code
51
51
  internal.set_building();
52
52
  internal.set_prerendering();
@@ -106,7 +106,10 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) {
106
106
  if (hash) {
107
107
  const fallback = await generate_fallback({
108
108
  manifest_path,
109
- env
109
+ env,
110
+ out_dir: config.outDir,
111
+ origin: config.prerender.origin,
112
+ assets: config.files.assets
110
113
  });
111
114
 
112
115
  const file = output_filename('/', true);
@@ -494,6 +497,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) {
494
497
  const public_env = filter_env(env, public_prefix, private_prefix);
495
498
  internal.set_private_env(private_env);
496
499
  internal.set_public_env(public_env);
500
+ internal.set_env(env);
497
501
  internal.set_manifest(manifest);
498
502
  internal.set_read_implementation((file) => createReadableStream(`${out}/server/${file}`));
499
503
 
@@ -11,6 +11,8 @@ import {
11
11
  create_node_analyser,
12
12
  get_page_options
13
13
  } from '../../exports/vite/static_analysis/index.js';
14
+ import { load_explicit_env } from '../env.js';
15
+ import { write_env } from './write_env.js';
14
16
 
15
17
  /**
16
18
  * Initialize SvelteKit's generated files that only depend on the config and mode.
@@ -87,6 +89,20 @@ export function all_types(config, mode) {
87
89
  write_non_ambient(config.kit, manifest_data);
88
90
  }
89
91
 
92
+ /**
93
+ * Generate modules and types for explicit env vars
94
+ * @param {import('types').ValidatedKitConfig} kit
95
+ * @param {string | null} entry
96
+ * @param {string} mode The Vite mode
97
+ */
98
+ export async function env(kit, entry, mode) {
99
+ const env_config = await load_explicit_env(kit, entry, mode);
100
+
101
+ write_env(kit, entry, env_config);
102
+
103
+ return env_config;
104
+ }
105
+
90
106
  /**
91
107
  * Regenerate __SERVER__/internal.js in response to src/{app.html,error.html,service-worker.js} changing
92
108
  * @param {import('types').ValidatedConfig} config
@@ -53,11 +53,17 @@ ${create_dynamic_types('public', env, prefixes)}
53
53
  * @param {string} mode The Vite mode
54
54
  */
55
55
  export function write_ambient(config, mode) {
56
- const env = get_env(config.env, mode);
57
- const { publicPrefix: public_prefix, privatePrefix: private_prefix } = config.env;
56
+ /** @type {string} */
57
+ let content;
58
58
 
59
- write_if_changed(
60
- path.join(config.outDir, 'ambient.d.ts'),
61
- template(env, { public_prefix, private_prefix })
62
- );
59
+ if (config.experimental.explicitEnvironmentVariables) {
60
+ content = `${GENERATED_COMMENT}\n/// <reference types="@sveltejs/kit" />`;
61
+ } else {
62
+ const env = get_env(config.env, mode);
63
+ const { publicPrefix: public_prefix, privatePrefix: private_prefix } = config.env;
64
+
65
+ content = template(env, { public_prefix, private_prefix });
66
+ }
67
+
68
+ write_if_changed(path.join(config.outDir, 'ambient.d.ts'), content);
63
69
  }
@@ -0,0 +1,32 @@
1
+ /** @import { EnvVarConfig } from '@sveltejs/kit' */
2
+ import path from 'node:path';
3
+ import { create_explicit_env_types } from '../env.js';
4
+ import { write_if_changed } from './utils.js';
5
+
6
+ const DOCS = '// See https://svelte.dev/docs/kit/environment-variables for more information';
7
+
8
+ /**
9
+ * Writes ambient declarations including types reference to @sveltejs/kit,
10
+ * and the existing environment variables in process.env to
11
+ * $env/static/private and $env/static/public
12
+ * @param {import('types').ValidatedKitConfig} kit
13
+ * @param {string | null} entry
14
+ * @param {Record<string, EnvVarConfig<any>> | null} env_config
15
+ */
16
+ export function write_env(kit, entry, env_config) {
17
+ const content = [];
18
+ const out = path.join(kit.outDir, 'env.d.ts');
19
+
20
+ if (entry && env_config) {
21
+ const relative = path.relative(kit.outDir, entry);
22
+ content.push(
23
+ `// This file is generated from ${relative}.\n${DOCS}`,
24
+ create_explicit_env_types(env_config, relative, 'private'),
25
+ create_explicit_env_types(env_config, relative, 'public')
26
+ );
27
+ } else {
28
+ content.push(DOCS);
29
+ }
30
+
31
+ write_if_changed(out, content.join('\n\n'));
32
+ }
@@ -100,7 +100,7 @@ export function write_root(manifest_data, config, output) {
100
100
  ${isSvelte5Plus() ? '<svelte:options runes={true} />' : ''}
101
101
  <script>
102
102
  import { setContext, ${isSvelte5Plus() ? '' : 'afterUpdate, '}onMount, tick } from 'svelte';
103
- import { browser } from '$app/environment';
103
+ import { browser } from '$app/env';
104
104
 
105
105
  // stores
106
106
  ${
@@ -30,9 +30,10 @@ const server_template = ({
30
30
  error_page
31
31
  }) => `
32
32
  import root from '../root.${isSvelte5Plus() ? 'js' : 'svelte'}';
33
- import { set_building, set_prerendering } from '__sveltekit/environment';
33
+ import { set_building, set_prerendering } from '$app/env/internal';
34
34
  import { set_assets } from '$app/paths/internal/server';
35
35
  import { set_manifest, set_read_implementation } from '__sveltekit/server';
36
+ import { set_env } from '__sveltekit/env';
36
37
  import { set_private_env, set_public_env } from '${runtime_directory}/shared-server.js';
37
38
 
38
39
  export const options = {
@@ -92,7 +93,7 @@ export async function get_hooks() {
92
93
  };
93
94
  }
94
95
 
95
- export { set_assets, set_building, set_manifest, set_prerendering, set_private_env, set_public_env, set_read_implementation };
96
+ export { set_assets, set_building, set_env, set_manifest, set_prerendering, set_private_env, set_public_env, set_read_implementation };
96
97
  `;
97
98
 
98
99
  // TODO need to re-run this whenever src/app.html or src/error.html are
@@ -57,6 +57,7 @@ export function get_tsconfig(kit) {
57
57
 
58
58
  const include = new Set([
59
59
  'ambient.d.ts', // careful: changing this name would be a breaking change, because it's referenced in the service-workers documentation
60
+ 'env.d.ts',
60
61
  'non-ambient.d.ts',
61
62
  './types/**/$types.d.ts',
62
63
  config_relative('vite.config.js'),
@@ -1 +1,14 @@
1
+ /** @import { EnvVarConfig } from '@sveltejs/kit' */
2
+
1
3
  export { sequence } from './sequence.js';
4
+
5
+ /**
6
+ * Utility for defining [environment variables](https://svelte.dev/docs/kit/environment-variables),
7
+ * which are made available via `$app/env/public` and `$app/env/private`.
8
+ * @template {Record<string, EnvVarConfig<any>>} T
9
+ * @param {T} variables
10
+ * @returns {T}
11
+ */
12
+ export function defineEnvVars(variables) {
13
+ return variables;
14
+ }
@@ -0,0 +1,71 @@
1
+ /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
2
+ /** @import { EnvVarConfig } from '@sveltejs/kit' */
3
+
4
+ import { stackless } from '../vite/utils.js';
5
+
6
+ const MISSING = {
7
+ message: `Value is missing. If it is optional, add a Standard Schema validator declaring it as such.`
8
+ };
9
+
10
+ const BAD_VALIDATOR = {
11
+ message: 'Variable was configured with a validator that does not implement Standard Schema'
12
+ };
13
+
14
+ const ASYNC_VALIDATOR = {
15
+ message: 'Variable uses an async validator, which is not supported'
16
+ };
17
+
18
+ /**
19
+ * @param {Record<string, EnvVarConfig<any>>} variables
20
+ * @param {string | undefined} value
21
+ * @param {string} name
22
+ * @param {Record<string, StandardSchemaV1.Issue[]>} issues
23
+ * @returns
24
+ */
25
+ export function validate(variables, value, name, issues) {
26
+ const config = variables[name] ?? {};
27
+ const validator = config.schema;
28
+
29
+ if (!validator) {
30
+ if (!value) issues[name] = [MISSING];
31
+ return value;
32
+ }
33
+
34
+ if (!validator['~standard']) {
35
+ issues[name] = [BAD_VALIDATOR];
36
+ return;
37
+ }
38
+
39
+ const result = validator['~standard'].validate(value);
40
+
41
+ if (result instanceof Promise) {
42
+ issues[name] = [ASYNC_VALIDATOR];
43
+ return;
44
+ }
45
+
46
+ if (result.issues) {
47
+ issues[name] = [...result.issues];
48
+ return;
49
+ }
50
+
51
+ return result.value;
52
+ }
53
+
54
+ /**
55
+ * @param {Record<string, StandardSchemaV1.Issue[]>} issues
56
+ */
57
+ export function handle_issues(issues) {
58
+ const entries = Object.entries(issues);
59
+
60
+ if (entries.length === 0) {
61
+ return;
62
+ }
63
+
64
+ let message = 'Invalid environment variables\n';
65
+
66
+ for (const [name, issues] of entries) {
67
+ message += `\n${name}\n${issues.map((issue) => ` - ${issue.message}`).join('\n')}\n`;
68
+ }
69
+
70
+ throw stackless(message);
71
+ }
@@ -0,0 +1,3 @@
1
+ // We re-export this so we can use it in the `$app/env/*` module declarations
2
+ // without the app needing to depend on this package
3
+ export { StandardSchemaV1 } from '@standard-schema/spec';