@schemic/surrealdb 0.1.0-alpha.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.
- package/LICENSE +21 -0
- package/README.md +103 -0
- package/lib/index.d.ts +1231 -0
- package/lib/index.js +5019 -0
- package/lib/index.js.map +1 -0
- package/package.json +68 -0
- package/src/cli/engine.ts +189 -0
- package/src/cli/introspect.ts +275 -0
- package/src/cli/lower.ts +370 -0
- package/src/cli/pull.ts +1049 -0
- package/src/cli/scaffold.ts +167 -0
- package/src/cli/struct.ts +0 -0
- package/src/cli/structure.ts +696 -0
- package/src/cli/surreal-connect.ts +112 -0
- package/src/cli/surreal-diff.ts +321 -0
- package/src/cli/surreal-filter.ts +67 -0
- package/src/config.ts +94 -0
- package/src/connection.ts +51 -0
- package/src/ddl.ts +931 -0
- package/src/driver/surql-type.ts +191 -0
- package/src/driver/surreal.ts +364 -0
- package/src/index.ts +99 -0
- package/src/kinds/explode.ts +201 -0
- package/src/kinds/portable.ts +116 -0
- package/src/kinds/registry.ts +177 -0
- package/src/pure.ts +2671 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// The files `schemic init` writes for a fresh SurrealDB project — a connections-only config via the
|
|
2
|
+
// surrealConnection factory, a sample s.* schema, a seed stub, and a .env.example. The CLI (dialect-
|
|
3
|
+
// free) calls surrealDriver.initScaffold() and writes these verbatim, then records the neutral snapshot.
|
|
4
|
+
|
|
5
|
+
const CONFIG = `import { defineConfig } from "@schemic/core/config";
|
|
6
|
+
import { surrealConnection } from "@schemic/surrealdb";
|
|
7
|
+
|
|
8
|
+
// Connections-only config: each named connection comes from a driver's \`<driver>Connection(...)\`
|
|
9
|
+
// factory, so there's no \`driver: "…"\` string to keep in sync. Values are explicit — read env here
|
|
10
|
+
// yourself (no implicit SURREAL_* magic). Add more named connections for multi-tenant / multi-DB setups.
|
|
11
|
+
export default defineConfig({
|
|
12
|
+
connections: {
|
|
13
|
+
default: surrealConnection({
|
|
14
|
+
schema: "./database/schema",
|
|
15
|
+
url: process.env.SURREAL_URL ?? "ws://127.0.0.1:8000/rpc",
|
|
16
|
+
namespace: process.env.SURREAL_NAMESPACE ?? "app",
|
|
17
|
+
database: process.env.SURREAL_DATABASE ?? "app",
|
|
18
|
+
username: process.env.SURREAL_USER,
|
|
19
|
+
password: process.env.SURREAL_PASS,
|
|
20
|
+
authLevel: "root", // "root" | "namespace" | "database"
|
|
21
|
+
// \`schemic check\` replays migrations into a throwaway engine; defaults to an ephemeral in-memory
|
|
22
|
+
// SurrealDB from your local \`surreal\` CLI. Override here, e.g.:
|
|
23
|
+
// check: { engine: "remote", db: { url: "ws://localhost:8000", namespace: "scratch" } },
|
|
24
|
+
}),
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const USER_TABLE = `import { defineTable, s, surql } from "@schemic/surrealdb";
|
|
30
|
+
|
|
31
|
+
// A SCHEMAFULL \`user\` table. Each field is a \`s.*\` builder (a drop-in for Zod's \`z.*\`) that also
|
|
32
|
+
// carries its SurrealQL DDL — \`s.email()\` validates the address, \`.unique()\` defines a UNIQUE index,
|
|
33
|
+
// and \`$default\`/\`$readonly\` map to the DEFAULT / READONLY clauses.
|
|
34
|
+
export const User = defineTable("user", {
|
|
35
|
+
name: s.string().$assert(surql\`string::len($value) > 0\`),
|
|
36
|
+
email: s.email().unique(),
|
|
37
|
+
createdAt: s.datetime().$default(surql\`time::now()\`).$readonly(),
|
|
38
|
+
}).schemafull();
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
const SEED = `import { RecordId, type Surreal } from "surrealdb";
|
|
42
|
+
|
|
43
|
+
/** Run with \`schemic seed\` — receives a connected client. */
|
|
44
|
+
export default async function seed(db: Surreal) {
|
|
45
|
+
await db.create(new RecordId("user", "ada")).content({
|
|
46
|
+
name: "Ada Lovelace",
|
|
47
|
+
email: "ada@example.com",
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
const ENV_EXAMPLE = `# Point these at your SurrealDB. The config reads them explicitly (no implicit SURREAL_* magic).
|
|
53
|
+
SURREAL_URL=ws://127.0.0.1:8000/rpc
|
|
54
|
+
SURREAL_NAMESPACE=app
|
|
55
|
+
SURREAL_DATABASE=app
|
|
56
|
+
SURREAL_USER=root
|
|
57
|
+
SURREAL_PASS=root
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
/** The dialect-specific files `schemic init` writes, keyed by project-relative path. */
|
|
61
|
+
export function initScaffold(): Record<string, string> {
|
|
62
|
+
return {
|
|
63
|
+
"schemic.config.ts": CONFIG,
|
|
64
|
+
"database/schema/tables/user.ts": USER_TABLE,
|
|
65
|
+
"database/seed.ts": SEED,
|
|
66
|
+
".env.example": ENV_EXAMPLE,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// --- `schemic new <kind> <name>` — per-kind starter modules ----------------------------------------
|
|
71
|
+
|
|
72
|
+
/** `"user_profile"` -> `"UserProfile"` (the exported const name). Non-alphanumerics split words. */
|
|
73
|
+
function pascalCase(name: string): string {
|
|
74
|
+
const parts = name.split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
|
75
|
+
const pascal = parts.map((p) => p[0].toUpperCase() + p.slice(1)).join("");
|
|
76
|
+
// Keep it a valid identifier: prefix a leading digit, fall back to a generic name.
|
|
77
|
+
return /^[0-9]/.test(pascal) ? `_${pascal}` : pascal || "Entity";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Author a starter module for `kind`/`name`. One template per authorable SurrealDB object, each a
|
|
82
|
+
* realistic starting point (not a bare stub). THROWS for a kind SurrealDB doesn't author standalone
|
|
83
|
+
* (notably `index`/`field`, which live inline on a table) or an unknown kind — the CLI surfaces the
|
|
84
|
+
* message. Used by `schemic new`; the CLI writes the result under the kind's display folder.
|
|
85
|
+
*/
|
|
86
|
+
export function scaffoldEntity(kind: string, name: string): string {
|
|
87
|
+
const C = pascalCase(name);
|
|
88
|
+
switch (kind) {
|
|
89
|
+
case "table":
|
|
90
|
+
return `import { defineTable, s, surql } from "@schemic/surrealdb";
|
|
91
|
+
|
|
92
|
+
// A SCHEMAFULL table. Each field is an \`s.*\` builder (a drop-in for Zod's \`z.*\`) that also carries
|
|
93
|
+
// its SurrealQL DDL. Add fields, then run \`schemic gen\`.
|
|
94
|
+
export const ${C} = defineTable("${name}", {
|
|
95
|
+
name: s.string(),
|
|
96
|
+
createdAt: s.datetime().$default(surql\`time::now()\`).$readonly(),
|
|
97
|
+
}).schemafull();
|
|
98
|
+
`;
|
|
99
|
+
case "relation":
|
|
100
|
+
return `import { defineRelation, s, surql } from "@schemic/surrealdb";
|
|
101
|
+
|
|
102
|
+
// A graph edge (\`TYPE RELATION\`). Chain \`.from(A).to(B)\` to restrict the endpoints and
|
|
103
|
+
// \`.enforced()\` to require both records to exist on RELATE.
|
|
104
|
+
export const ${C} = defineRelation("${name}", {
|
|
105
|
+
since: s.datetime().$default(surql\`time::now()\`),
|
|
106
|
+
});
|
|
107
|
+
// .from(SomeTable).to(OtherTable).enforced()
|
|
108
|
+
`;
|
|
109
|
+
case "view":
|
|
110
|
+
return `import { defineView, surql } from "@schemic/surrealdb";
|
|
111
|
+
|
|
112
|
+
// A pre-computed (materialized) view — SurrealDB keeps its rows in sync with the source query.
|
|
113
|
+
export const ${C} = defineView(
|
|
114
|
+
"${name}",
|
|
115
|
+
surql\`SELECT * FROM thing WHERE true\`,
|
|
116
|
+
);
|
|
117
|
+
`;
|
|
118
|
+
case "function":
|
|
119
|
+
return `import { defineFunction, s, surql } from "@schemic/surrealdb";
|
|
120
|
+
|
|
121
|
+
// A custom function (\`fn::${name}\`). Functions referenced from fields/events/access become
|
|
122
|
+
// dependency edges, so a caller emits after its callee.
|
|
123
|
+
export const ${C} = defineFunction("${name}", { arg: s.string() })
|
|
124
|
+
.returns(s.string())
|
|
125
|
+
.body(surql\`RETURN $arg\`);
|
|
126
|
+
`;
|
|
127
|
+
case "access":
|
|
128
|
+
return `import { defineAccess, surql } from "@schemic/surrealdb";
|
|
129
|
+
|
|
130
|
+
// A RECORD access method (signup/signin). See \`.jwt({ … })\` / \`.bearer({ … })\` for other types.
|
|
131
|
+
export const ${C} = defineAccess("${name}")
|
|
132
|
+
.record()
|
|
133
|
+
.signup(surql\`CREATE user SET email = $email, pass = crypto::argon2::generate($pass)\`)
|
|
134
|
+
.signin(surql\`SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(pass, $pass)\`)
|
|
135
|
+
.duration({ token: "1h", session: "12h" });
|
|
136
|
+
`;
|
|
137
|
+
case "event":
|
|
138
|
+
return `import { defineEvent, surql } from "@schemic/surrealdb";
|
|
139
|
+
|
|
140
|
+
// A standalone event — replace "thing" with the table it fires on. \`then\` takes one expression or
|
|
141
|
+
// an ordered array.
|
|
142
|
+
export const ${C} = defineEvent("thing", "${name}", {
|
|
143
|
+
when: surql\`$event = "CREATE"\`,
|
|
144
|
+
then: surql\`CREATE log SET at = time::now()\`,
|
|
145
|
+
});
|
|
146
|
+
`;
|
|
147
|
+
case "analyzer":
|
|
148
|
+
return `import { defineAnalyzer } from "@schemic/surrealdb";
|
|
149
|
+
|
|
150
|
+
// A text analyzer for FULLTEXT search. A \`.index(field, { fulltext: { analyzer: "${name}" } })\` on a
|
|
151
|
+
// table depends on it.
|
|
152
|
+
export const ${C} = defineAnalyzer("${name}", {
|
|
153
|
+
tokenizers: ["blank"],
|
|
154
|
+
filters: ["lowercase"],
|
|
155
|
+
});
|
|
156
|
+
`;
|
|
157
|
+
case "index":
|
|
158
|
+
case "field":
|
|
159
|
+
throw new Error(
|
|
160
|
+
`SurrealDB ${kind}s are authored inline on a table, not as their own file — add it to a table (e.g. \`defineTable("…", { … }).index("${name}", [field])\` or \`s.string().unique()\`). Try \`schemic new table <name>\`.`,
|
|
161
|
+
);
|
|
162
|
+
default:
|
|
163
|
+
throw new Error(
|
|
164
|
+
`the surrealdb driver can't scaffold a "${kind}" — known kinds: table, relation, view, function, access, event, analyzer.`,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
Binary file
|