aboutsummaryrefslogtreecommitdiff
path: root/packages/integrations/cloudflare/src/utils/local-runtime.ts
diff options
context:
space:
mode:
authorGravatar Alexander Niebuhr <alexander@nbhr.io> 2024-03-28 09:17:53 +0100
committerGravatar GitHub <noreply@github.com> 2024-03-28 09:17:53 +0100
commit8ef3050f1461bb824f095af5157624bfb68ef63c (patch)
treefa14f03b307c4fea4218f89ce75fceea48790a81 /packages/integrations/cloudflare/src/utils/local-runtime.ts
parent08f6167c012a2c61da5055f350acdf62a8b12b8f (diff)
downloadastro-8ef3050f1461bb824f095af5157624bfb68ef63c.tar.gz
astro-8ef3050f1461bb824f095af5157624bfb68ef63c.tar.zst
astro-8ef3050f1461bb824f095af5157624bfb68ef63c.zip
feat(cloudflare): major refactor for v10 (#159)
Co-authored-by: Matthew Phillips <361671+matthewp@users.noreply.github.com> Co-authored-by: Emanuele Stoppa <602478+ematipico@users.noreply.github.com> Co-authored-by: Erika <3019731+Princesseuh@users.noreply.github.com> Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
Diffstat (limited to 'packages/integrations/cloudflare/src/utils/local-runtime.ts')
-rw-r--r--packages/integrations/cloudflare/src/utils/local-runtime.ts368
1 files changed, 0 insertions, 368 deletions
diff --git a/packages/integrations/cloudflare/src/utils/local-runtime.ts b/packages/integrations/cloudflare/src/utils/local-runtime.ts
deleted file mode 100644
index 8318fb1e3..000000000
--- a/packages/integrations/cloudflare/src/utils/local-runtime.ts
+++ /dev/null
@@ -1,368 +0,0 @@
-import type {
- D1Database,
- DurableObjectNamespace,
- IncomingRequestCfProperties,
- KVNamespace,
- R2Bucket,
-} from '@cloudflare/workers-types/experimental';
-import type { AstroConfig, AstroIntegrationLogger } from 'astro';
-import type { ExternalServer, Json, ReplaceWorkersTypes, WorkerOptions } from 'miniflare';
-import type { Options } from '../index.js';
-
-import assert from 'node:assert';
-import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
-import { fileURLToPath } from 'node:url';
-import TOML from '@iarna/toml';
-import { AstroError } from 'astro/errors';
-import dotenv from 'dotenv';
-import { Miniflare } from 'miniflare';
-
-interface NodeJSError extends Error {
- code?: string;
-}
-
-type BASE_RUNTIME = {
- mode: 'local';
- type: 'pages' | 'workers';
- persistTo: string;
- bindings: Record<
- string,
- | { type: 'var'; value: string | Json }
- | { type: 'kv' }
- | { type: 'r2' }
- | {
- type: 'd1';
- }
- | {
- type: 'durable-object';
- className: string;
- service?: {
- name: string;
- env?: string;
- };
- }
- | ({
- type: 'service';
- } & (
- | {
- address: string;
- protocol?: 'http' | 'https';
- }
- | {
- service: string;
- }
- ))
- >;
-};
-
-export type RUNTIME =
- | {
- mode: BASE_RUNTIME['mode'];
- type: 'pages';
- persistTo: BASE_RUNTIME['persistTo'];
- bindings: BASE_RUNTIME['bindings'];
- }
- | {
- mode: BASE_RUNTIME['mode'];
- type: 'workers';
- persistTo: BASE_RUNTIME['persistTo'];
- };
-
-class LocalRuntime {
- private _astroConfig: AstroConfig;
- private _logger: AstroIntegrationLogger;
- private _miniflare: Miniflare;
-
- private miniflareBindings:
- | Record<
- string,
- | D1Database
- | ReplaceWorkersTypes<R2Bucket>
- | ReplaceWorkersTypes<KVNamespace>
- | ReplaceWorkersTypes<DurableObjectNamespace>
- | Json
- >
- | undefined;
- private secrets: Record<string, string> | undefined;
- private cfObject: IncomingRequestCfProperties | undefined;
-
- public constructor(
- astroConfig: AstroConfig,
- runtimeConfig: BASE_RUNTIME,
- logger: AstroIntegrationLogger
- ) {
- this._astroConfig = astroConfig;
- this._logger = logger;
-
- const varBindings: Required<Pick<WorkerOptions, 'bindings'>>['bindings'] = {};
- const kvBindings: Required<Pick<WorkerOptions, 'kvNamespaces'>>['kvNamespaces'] = [];
- const d1Bindings: Required<Pick<WorkerOptions, 'd1Databases'>>['d1Databases'] = [];
- const r2Bindings: Required<Pick<WorkerOptions, 'r2Buckets'>>['r2Buckets'] = [];
- const durableObjectBindings: Required<Pick<WorkerOptions, 'durableObjects'>>['durableObjects'] =
- {};
- const serviceBindings: Required<Pick<WorkerOptions, 'serviceBindings'>>['serviceBindings'] = {};
-
- for (const bindingName in runtimeConfig.bindings) {
- const bindingData = runtimeConfig.bindings[bindingName];
- switch (bindingData.type) {
- case 'var':
- varBindings[bindingName] = bindingData.value;
- break;
- case 'kv':
- kvBindings.push(bindingName);
- break;
- case 'd1':
- d1Bindings.push(bindingName);
- break;
- case 'r2':
- r2Bindings.push(bindingName);
- break;
- case 'durable-object':
- durableObjectBindings[bindingName] = {
- className: bindingData.className,
- scriptName: bindingData.service?.name,
- };
- break;
- case 'service':
- if ('address' in bindingData) {
- // Pages mode
- const isHttps = bindingData.protocol === 'https';
-
- const serviceBindingConfig: ExternalServer = isHttps
- ? { address: bindingData.address, https: {} }
- : { address: bindingData.address, http: {} };
-
- serviceBindings[bindingName] = {
- external: serviceBindingConfig,
- };
- } else if ('service' in bindingData) {
- // Worker mode
- serviceBindings[bindingName] = bindingData.service;
- }
- break;
- }
- }
-
- this._miniflare = new Miniflare({
- cachePersist: `${runtimeConfig.persistTo}/cache`,
- d1Persist: `${runtimeConfig.persistTo}/d1`,
- r2Persist: `${runtimeConfig.persistTo}/r2`,
- kvPersist: `${runtimeConfig.persistTo}/kv`,
- durableObjectsPersist: `${runtimeConfig.persistTo}/do`,
- workers: [
- {
- name: 'worker',
- script: '',
- modules: true,
- cacheWarnUsage: true,
- cache: true,
- bindings: varBindings,
- d1Databases: d1Bindings,
- r2Buckets: r2Bindings,
- kvNamespaces: kvBindings,
- durableObjects: durableObjectBindings,
- serviceBindings: serviceBindings,
- },
- ],
- });
- }
-
- public async getBindings() {
- await this._miniflare.ready;
- if (!this.miniflareBindings) {
- this.miniflareBindings = await this._miniflare.getBindings();
- }
- return this.miniflareBindings;
- }
-
- public async getSecrets() {
- await this._miniflare.ready;
- if (!this.secrets) {
- try {
- this.secrets = dotenv.parse(
- readFileSync(fileURLToPath(new URL('./.dev.vars', this._astroConfig.root)))
- );
- } catch (error) {
- const e = error as NodeJSError;
- if (e.code === 'ENOENT') {
- this._logger.info(
- 'There is no `.dev.vars` file in the root directory, if you have encrypted secrets or environmental variables Cloudflare recommends you to put them in this file'
- );
- this.secrets = {};
- } else {
- throw new AstroError('Failed to load secrets file', e.message);
- }
- }
- }
- return this.secrets;
- }
-
- public async getCaches() {
- await this._miniflare.ready;
- return this._miniflare.getCaches();
- }
-
- public async getCF() {
- await this._miniflare.ready;
-
- const MAX_CACHE_AGE = 1000 * 60 * 60 * 24 * 7; // 7 days;
- // Try load cached cfObject, if this fails, we'll catch the error and refetch.
- // If this succeeds, and the file is stale, that's fine: it's very likely
- // we'll be fetching the same data anyways.
- try {
- const cachedCFObject = JSON.parse(
- readFileSync(fileURLToPath(new URL('cf.json', this._astroConfig.cacheDir)), 'utf8')
- );
- const cfObjectStats = statSync(fileURLToPath(new URL('cf.json', this._astroConfig.cacheDir)));
- assert(Date.now() - cfObjectStats.mtimeMs <= MAX_CACHE_AGE);
- this.cfObject = cachedCFObject;
- } catch {}
-
- const CF_ENDPOINT = 'https://workers.cloudflare.com/cf.json';
- if (!this.cfObject) {
- this.cfObject = await fetch(CF_ENDPOINT).then((res) => res.json());
- if (!existsSync(this._astroConfig.cacheDir)) {
- mkdirSync(this._astroConfig.cacheDir);
- }
- writeFileSync(
- fileURLToPath(new URL('cf.json', this._astroConfig.cacheDir)),
- JSON.stringify(this.cfObject),
- 'utf8'
- );
- }
- return this.cfObject;
- }
-
- public async dispose() {
- await this._miniflare.dispose();
- }
-}
-
-export class LocalWorkersRuntime extends LocalRuntime {
- constructor(
- astroConfig: AstroConfig,
- runtimeConfig: Extract<RUNTIME, { type: 'workers' }>,
- logger: AstroIntegrationLogger
- ) {
- let _wranglerConfig: CF_RawConfig | undefined;
- try {
- _wranglerConfig = TOML.parse(
- readFileSync(fileURLToPath(new URL('./wrangler.toml', astroConfig.root)), 'utf-8').replace(
- /\r\n/g,
- '\n'
- )
- ) as unknown as CF_RawConfig;
- } catch (error) {
- const e = error as NodeJSError;
- if (e.code === 'ENOENT') {
- logger.error('Missing file `wrangler.toml in root directory`');
- } else {
- throw new AstroError('Failed to load wrangler config', e.message);
- }
- }
- const runtimeConfigWithWrangler: BASE_RUNTIME = {
- ...runtimeConfig,
- bindings: {},
- };
- if (_wranglerConfig?.vars) {
- for (const key in _wranglerConfig.vars) {
- runtimeConfigWithWrangler.bindings[key] = {
- type: 'var',
- value: _wranglerConfig.vars[key],
- };
- }
- }
- if (_wranglerConfig?.kv_namespaces) {
- for (const ns of _wranglerConfig.kv_namespaces) {
- runtimeConfigWithWrangler.bindings[ns.binding] = {
- type: 'kv',
- };
- }
- }
- if (_wranglerConfig?.d1_databases) {
- for (const db of _wranglerConfig.d1_databases) {
- runtimeConfigWithWrangler.bindings[db.binding] = {
- type: 'd1',
- };
- }
- }
- if (_wranglerConfig?.r2_buckets) {
- for (const bucket of _wranglerConfig.r2_buckets) {
- runtimeConfigWithWrangler.bindings[bucket.binding] = {
- type: 'r2',
- };
- }
- }
- if (_wranglerConfig?.durable_objects) {
- for (const durableObject of _wranglerConfig.durable_objects.bindings) {
- runtimeConfigWithWrangler.bindings[durableObject.name] = {
- type: 'durable-object',
- className: durableObject.class_name,
- service: durableObject.script_name
- ? {
- name: durableObject.script_name,
- }
- : undefined,
- };
- }
- }
- if (_wranglerConfig?.services) {
- for (const service of _wranglerConfig.services) {
- runtimeConfigWithWrangler.bindings[service.binding] = {
- type: 'service',
- service: service.service,
- };
- }
- }
-
- super(astroConfig, runtimeConfigWithWrangler, logger);
- }
-}
-
-export class LocalPagesRuntime extends LocalRuntime {
- // biome-ignore lint/complexity/noUselessConstructor: not types information yet, so we need to disable the rule for the time being
- public constructor(
- astroConfig: AstroConfig,
- runtimeConfig: Extract<RUNTIME, { type: 'pages' }>,
- logger: AstroIntegrationLogger
- ) {
- super(astroConfig, runtimeConfig, logger);
- }
-}
-
-let localRuntime: LocalPagesRuntime | LocalWorkersRuntime | undefined;
-export function getLocalRuntime(
- astroConfig: AstroConfig,
- runtimeConfig: RUNTIME,
- logger: AstroIntegrationLogger
-): LocalPagesRuntime | LocalWorkersRuntime {
- if (localRuntime) return localRuntime;
-
- if (runtimeConfig.type === 'pages') {
- localRuntime = new LocalPagesRuntime(astroConfig, runtimeConfig, logger);
- } else {
- localRuntime = new LocalWorkersRuntime(astroConfig, runtimeConfig, logger);
- }
-
- return localRuntime;
-}
-
-export function getRuntimeConfig(userConfig?: Options['runtime']): { mode: 'off' } | RUNTIME {
- if (!userConfig || userConfig.mode === 'off') return { mode: 'off' };
-
- // we know that we have `mode: local` below
- if (userConfig.type === 'pages')
- return {
- mode: 'local',
- type: 'pages',
- persistTo: userConfig.persistTo ?? '.wrangler/state/v3',
- bindings: userConfig.bindings ?? {},
- };
-
- // we know that we have `type: workers` below
- return {
- mode: 'local',
- type: 'workers',
- persistTo: userConfig.persistTo ?? '.wrangler/state/v3',
- };
-}