Skip to content

Commit

Permalink
feat: add durable storage MVP
Browse files Browse the repository at this point in the history
  • Loading branch information
MasterPtato authored and Blckbrry-Pi committed May 21, 2024
1 parent b10d158 commit 832f88c
Show file tree
Hide file tree
Showing 26 changed files with 928 additions and 70 deletions.
2 changes: 1 addition & 1 deletion artifacts/module_schema.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"type":"object","properties":{"status":{"enum":["beta","end_of_life","maintenance","preview","stable"],"type":"string"},"name":{"description":"The human readable name of the module.","type":"string"},"description":{"description":"A short description of the module.","type":"string"},"icon":{"description":"The [Font Awesome](https://fontawesome.com/icons) icon name of the module.","type":"string"},"tags":{"description":"The tags associated with this module.","type":"array","items":{"type":"string"}},"authors":{"description":"The GitHub handle of the authors of the module.","type":"array","items":{"type":"string"}},"scripts":{"type":"object","additionalProperties":{"$ref":"#/definitions/ScriptConfig"}},"errors":{"type":"object","additionalProperties":{"$ref":"#/definitions/ErrorConfig"}},"dependencies":{"type":"object","additionalProperties":{"$ref":"#/definitions/DependencyConfig"}}},"additionalProperties":false,"required":["errors","scripts"],"definitions":{"ScriptConfig":{"type":"object","properties":{"name":{"description":"The human readable name of the script.","type":"string"},"description":{"description":"A short description of the script.","type":"string"},"public":{"description":"If the script can be called from the public HTTP interface.\n\nIf enabled, ensure that authentication & rate limits are configured for\nthis endpoints. See the `user` and `rate_limit` modules.","default":false,"type":"boolean"}},"additionalProperties":false},"ErrorConfig":{"type":"object","properties":{"name":{"description":"The human readable name of the error.","type":"string"},"description":{"description":"A short description of the error.","type":"string"}},"additionalProperties":false},"DependencyConfig":{"type":"object","additionalProperties":false}},"$schema":"http://json-schema.org/draft-07/schema#"}
{"type":"object","properties":{"status":{"enum":["beta","end_of_life","maintenance","preview","stable"],"type":"string"},"name":{"description":"The human readable name of the module.","type":"string"},"description":{"description":"A short description of the module.","type":"string"},"icon":{"description":"The [Font Awesome](https://fontawesome.com/icons) icon name of the module.","type":"string"},"tags":{"description":"The tags associated with this module.","type":"array","items":{"type":"string"}},"authors":{"description":"The GitHub handle of the authors of the module.","type":"array","items":{"type":"string"}},"scripts":{"type":"object","additionalProperties":{"$ref":"#/definitions/ScriptConfig"}},"actors":{"type":"object","additionalProperties":{"$ref":"#/definitions/ActorConfig"}},"errors":{"type":"object","additionalProperties":{"$ref":"#/definitions/ErrorConfig"}},"dependencies":{"type":"object","additionalProperties":{"$ref":"#/definitions/DependencyConfig"}}},"additionalProperties":false,"required":["errors","scripts"],"definitions":{"ScriptConfig":{"type":"object","properties":{"name":{"description":"The human readable name of the script.","type":"string"},"description":{"description":"A short description of the script.","type":"string"},"public":{"description":"If the script can be called from the public HTTP interface.\n\nIf enabled, ensure that authentication & rate limits are configured for\nthis endpoints. See the `user` and `rate_limit` modules.","default":false,"type":"boolean"}},"additionalProperties":false},"ActorConfig":{"type":"object","properties":{"storage_id":{"description":"A globally unique string for storing data for this actor.\n\n**IMPORTANT** Changing this will effectively unlink all data stored in this actor. Changing it back to\nthe old value will restore the data.","type":"string"}},"additionalProperties":false,"required":["storage_id"]},"ErrorConfig":{"type":"object","properties":{"name":{"description":"The human readable name of the error.","type":"string"},"description":{"description":"A short description of the error.","type":"string"}},"additionalProperties":false},"DependencyConfig":{"type":"object","additionalProperties":false}},"$schema":"http://json-schema.org/draft-07/schema#"}
2 changes: 1 addition & 1 deletion artifacts/runtime_archive.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion deno.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"cli:compile": "deno task artifacts:build:all && deno compile --check --allow-net --allow-read --allow-env --allow-run --allow-write --allow-sys --output dist/cli src/cli/main.ts",

// Installs the CLI on the local machine
"cli:install": "deno task artifacts:build:all && deno install --global --check --allow-net --allow-read --allow-env --allow-run --allow-write --allow-sys --name opengb --force src/cli/main.ts",
"cli:install": "deno task artifacts:build:all && deno install --check --allow-net --allow-read --allow-env --allow-run --allow-write --allow-sys --name opengb --force src/cli/main.ts",

// Generates schema
"artifacts:build:all": "deno task artifacts:build:schema && deno task artifacts:build:runtime_archive",
Expand Down
2 changes: 1 addition & 1 deletion src/artifacts/build_runtime_archive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ if (!dirname) throw new Error("Missing dirname");
const rootSrc = resolve(dirname, "..", "..");

const files = await glob.glob([
"src/{runtime,types}/*.ts",
"src/{runtime,types,dynamic}/*.ts",
"src/deps.ts",
"src/utils/db.ts",
], { cwd: rootSrc });
Expand Down
70 changes: 63 additions & 7 deletions src/build/entrypoint.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { dirname, resolve } from "../deps.ts";
import { Project } from "../project/mod.ts";
import { dirname, fromFileUrl, resolve } from "../deps.ts";
import { ACTOR_PATH, Project, genActorCaseConversionMapPath } from "../project/mod.ts";
import { ENTRYPOINT_PATH, GITIGNORE_PATH, RUNTIME_CONFIG_PATH, RUNTIME_PATH, genDependencyCaseConversionMapPath, genPath, genPrismaOutputBundle, genRuntimeModPath } from "../project/project.ts";
import { CommandError } from "../error/mod.ts";
import { autoGenHeader } from "./misc.ts";
import { BuildOpts, DbDriver, Runtime } from "./mod.ts";
import { dedent } from "./deps.ts";

// Read source files as strings
const ACTOR_SOURCE = await Deno.readTextFile(resolve(dirname(fromFileUrl(import.meta.url)), "../dynamic/actor.ts"));
const ACTOR_CF_SOURCE = await Deno.readTextFile(
resolve(dirname(fromFileUrl(import.meta.url)), "../dynamic/actor_cf.ts"),
);

export async function generateEntrypoint(project: Project, opts: BuildOpts) {
const runtimeModPath = genRuntimeModPath(project);

Expand All @@ -23,19 +29,27 @@ export async function generateEntrypoint(project: Project, opts: BuildOpts) {
} else if (opts.dbDriver == DbDriver.NeonServerless) {
imports += `
// Import Prisma serverless adapter for Neon
import * as neon from "https://esm.sh/@neondatabase/serverless@^0.9.0";
import { PrismaNeonHTTP } from "https://esm.sh/@prisma/adapter-neon@^5.10.2";
import * as neon from "https://esm.sh/@neondatabase/serverless@^0.9.3";
import { PrismaNeonHTTP } from "https://esm.sh/@prisma/adapter-neon@^5.13.0";
`;
}

let compat = "";
let actorSource = `
import { Config } from "${runtimeModPath}";
import config from "./runtime_config.ts";
`;

if (opts.runtime == Runtime.Deno) {
compat += `
// Create module for Prisma compatibility
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
`;

actorSource += ACTOR_SOURCE;
} else {
actorSource += ACTOR_CF_SOURCE;
}

// Generate config.ts
Expand All @@ -60,11 +74,21 @@ export async function generateEntrypoint(project: Project, opts: BuildOpts) {
${autoGenHeader()}
import { Runtime } from "${runtimeModPath}";
import { dependencyCaseConversionMap } from "${genDependencyCaseConversionMapPath(project)}";
import { actorCaseConversionMap } from "${genActorCaseConversionMapPath(project)}";
import type { DependenciesSnake, DependenciesCamel } from "./dependencies.d.ts";
import type { ActorsSnake, ActorsCamel } from "./actors.d.ts";
import config from "./runtime_config.ts";
import { ACTOR_DRIVER } from "./actor.ts";
async function main() {
const runtime = new Runtime<DependenciesSnake, DependenciesCamel>(config, dependencyCaseConversionMap);
const runtime = new Runtime<
DependenciesSnake, DependenciesCamel, ActorsSnake, ActorsCamel
>(
config,
ACTOR_DRIVER,
dependencyCaseConversionMap,
actorCaseConversionMap,
);
await runtime.serve();
}
Expand All @@ -81,11 +105,21 @@ export async function generateEntrypoint(project: Project, opts: BuildOpts) {
import { Runtime } from "${runtimeModPath}";
import { RuntimeError } from "${errorTsPath}";
import { dependencyCaseConversionMap } from "${genDependencyCaseConversionMapPath(project)}";
import { actorCaseConversionMap } from "${genActorCaseConversionMapPath(project)}";
import type { DependenciesSnake, DependenciesCamel } from "./dependencies.d.ts";
import type { ActorsSnake, ActorsCamel } from "./actors.d.ts";
import config from "./runtime_config.ts";
import { ACTOR_DRIVER } from "./actor.ts";
import { serverHandler } from "${serverTsPath}";
const RUNTIME = new Runtime<DependenciesSnake, DependenciesCamel>(config, dependencyCaseConversionMap);
const RUNTIME = new Runtime<
DependenciesSnake, DependenciesCamel, ActorsSnake, ActorsCamel
>(
config,
ACTOR_DRIVER,
dependencyCaseConversionMap,
actorCaseConversionMap,
);
const SERVER_HANDLER = serverHandler(RUNTIME);
export default {
Expand All @@ -109,19 +143,27 @@ export async function generateEntrypoint(project: Project, opts: BuildOpts) {
});
}
}
// Export durable object binding
export { __GlobalDurableObject } from "./actor.ts";
`;
}

// Write files
const distDir = resolve(project.path, "_gen");
const configPath = genPath(project, RUNTIME_CONFIG_PATH);
const entrypointPath = genPath(project, ENTRYPOINT_PATH);
await Deno.mkdir(dirname(configPath), { recursive: true });
const actorPath = genPath(project, ACTOR_PATH);

await Deno.mkdir(distDir, { recursive: true });
await Deno.writeTextFile(configPath, configSource);
await Deno.writeTextFile(entrypointPath, entrypointSource);
await Deno.writeTextFile(actorPath, actorSource);
await Deno.writeTextFile(
genPath(project, GITIGNORE_PATH),
".",
);
await Deno.writeTextFile(actorPath, actorSource);

// Format files
const fmtOutput = await new Deno.Command("deno", {
Expand Down Expand Up @@ -153,6 +195,20 @@ function generateModImports(project: Project, opts: BuildOpts) {
}
modConfig += "},";

// Generate actor configs
modConfig += "actors: {";
for (const actor of mod.actors.values()) {
const actorIdent = `modules$$${mod.name}$$${actor.name}$$Actor`;

modImports += `import { Actor as ${actorIdent} } from '${mod.path}/actors/${actor.name}.ts';\n`;

modConfig += `${JSON.stringify(actor.name)}: {`;
modConfig += `actor: ${actorIdent},`;
modConfig += `storageId: ${JSON.stringify(actor.config.storage_id)},`;
modConfig += `},`;
}
modConfig += "},";

// Generate error configs
modConfig += `errors: ${JSON.stringify(mod.config.errors)},`;

Expand Down
2 changes: 1 addition & 1 deletion src/build/gen/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ export { GeneratedCodeBuilder } from "./code_builder.ts";
export { compileModuleHelper } from "./module.ts";

export { compilePublic } from "./public.ts";
export { compileTypeHelpers } from "./type.ts";
export { compileTypeHelpers, compileActorTypeHelpers } from "./type.ts";
81 changes: 77 additions & 4 deletions src/build/gen/module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { hasUserConfigSchema, Module, moduleHelperGen, Project } from "../../project/mod.ts";
import { genActorCaseConversionMapPath, genActorTypedefPath, hasUserConfigSchema, Module, moduleHelperGen, Project } from "../../project/mod.ts";
import { GeneratedCodeBuilder } from "./mod.ts";
import {
genDependencyCaseConversionMapPath,
Expand All @@ -7,6 +7,7 @@ import {
genPath,
genPrismaOutputBundle,
genRuntimeModPath,
genActorPath,
RUNTIME_CONFIG_PATH,
} from "../../project/project.ts";
import { camelify } from "../../types/case_conversions.ts";
Expand All @@ -19,14 +20,24 @@ export async function compileModuleHelper(

const runtimePath = helper.relative(genRuntimeModPath(project));
const dependencyCaseConversionMapPath = helper.relative(genDependencyCaseConversionMapPath(project));
const actorCaseConversionMapPath = helper.relative(genActorCaseConversionMapPath(project));
const runtimeConfigPath = helper.relative(genPath(project, RUNTIME_CONFIG_PATH));

// Import block
const importBlock = helper.chunk.withNewlinesPerChunk(1)
.append`
import { RuntimeError, ModuleContext as ModuleContextInner, Runtime, TestContext as TestContextInner, ScriptContext as ScriptContextInner } from "${runtimePath}";
import {
RuntimeError,
ModuleContext as ModuleContextInner,
Runtime,
TestContext as TestContextInner,
ScriptContext as ScriptContextInner,
} from "${runtimePath}";
import config from "${runtimeConfigPath}";
import { dependencyCaseConversionMap } from "${dependencyCaseConversionMapPath}";
import { actorCaseConversionMap } from "${actorCaseConversionMapPath}";
import { ActorBase, ACTOR_DRIVER } from "${genActorPath(project)}";
`;

// Type helpers
Expand All @@ -43,13 +54,14 @@ export async function compileModuleHelper(

// Common exports
helper.chunk.append`
export { RuntimeError };
export { RuntimeError, ActorBase };
`;

// Gen blocks
const { userConfigType } = await genUserConfig(project, module, helper);
genPublic(project, module, helper);
genDependencies(project, module, helper);
genActors(project, module, helper);
genModule(project, module, helper, importBlock, userConfigType);
genTest(project, module, helper, userConfigType);
genScript(project, module, helper, userConfigType);
Expand Down Expand Up @@ -125,6 +137,53 @@ function genDependencies(
);
}

function genActors(
project: Project,
module: Module,
helper: GeneratedCodeBuilder,
) {
const typedefPath = genActorTypedefPath(project);

helper.append`
import type {
ActorsSnake as ActorsSnakeFull,
ActorsCamel as ActorsCamelFull,
} from "${typedefPath}";
`;

const actorsTypedefSnake = helper.chunk.withNewlinesPerChunk(1);
const actorsTypedefCamel = helper.chunk.withNewlinesPerChunk(1);

const moduleNameSnake = module.name;
const moduleNameCamel = camelify(module.name);

for (const dependencyName of Object.keys(module.config.dependencies || {})) {
const dependencyNameSnake = dependencyName;
const dependencyNameCamel = camelify(dependencyName);

actorsTypedefSnake.append`
${dependencyNameSnake}: ActorsSnakeFull["${dependencyNameSnake}"];
`;
actorsTypedefCamel.append`
${dependencyNameCamel}: ActorsCamelFull["${dependencyNameCamel}"];
`;
}

actorsTypedefSnake.prepend`${moduleNameSnake}: ActorsSnakeFull["${moduleNameSnake}"];`;
actorsTypedefCamel.prepend`${moduleNameCamel}: ActorsCamelFull["${moduleNameCamel}"];`;

GeneratedCodeBuilder.wrap(
"interface ActorsSnake {",
actorsTypedefSnake,
"}",
);
GeneratedCodeBuilder.wrap(
"interface ActorsCamel {",
actorsTypedefCamel,
"}",
);
}

function genModule(
project: Project,
module: Module,
Expand All @@ -148,6 +207,8 @@ function genModule(
export type ModuleContext = ModuleContextInner<
DependenciesSnake,
DependenciesCamel,
ActorsSnake,
ActorsCamel,
${userConfigType},
${module.db ? "prisma.PrismaClient" : "undefined"}
>;
Expand All @@ -167,6 +228,8 @@ function genTest(
export type TestContext = TestContextInner<
DependenciesSnake,
DependenciesCamel,
ActorsSnake,
ActorsCamel,
${userConfigType},
${module.db ? "prisma.PrismaClient" : "undefined"}
>;
Expand All @@ -177,7 +240,15 @@ function genTest(
.append`export type TestFn = (ctx: TestContext) => Promise<void>;`
.append`
export function test(name: string, fn: TestFn) {
Runtime.test(config, "${module.name}", name, fn, dependencyCaseConversionMap);
Runtime.test(
config,
ACTOR_DRIVER,
"${module.name}",
name,
fn,
dependencyCaseConversionMap,
actorCaseConversionMap,
);
}
`;
}
Expand All @@ -195,6 +266,8 @@ function genScript(
export type ScriptContext = ScriptContextInner<
DependenciesSnake,
DependenciesCamel,
ActorsSnake,
ActorsCamel,
${userConfigType},
${module.db ? "prisma.PrismaClient" : "undefined"}
>;
Expand Down

0 comments on commit 832f88c

Please sign in to comment.