@obh/forge 0.2.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 +146 -0
- package/dist/capabilities/analytics.d.ts +3 -0
- package/dist/capabilities/analytics.d.ts.map +1 -0
- package/dist/capabilities/analytics.js +91 -0
- package/dist/capabilities/analytics.js.map +1 -0
- package/dist/capabilities/api-keys.d.ts +3 -0
- package/dist/capabilities/api-keys.d.ts.map +1 -0
- package/dist/capabilities/api-keys.js +90 -0
- package/dist/capabilities/api-keys.js.map +1 -0
- package/dist/capabilities/audit.d.ts +3 -0
- package/dist/capabilities/audit.d.ts.map +1 -0
- package/dist/capabilities/audit.js +74 -0
- package/dist/capabilities/audit.js.map +1 -0
- package/dist/capabilities/entitlements.d.ts +3 -0
- package/dist/capabilities/entitlements.d.ts.map +1 -0
- package/dist/capabilities/entitlements.js +94 -0
- package/dist/capabilities/entitlements.js.map +1 -0
- package/dist/capabilities/events.d.ts +3 -0
- package/dist/capabilities/events.d.ts.map +1 -0
- package/dist/capabilities/events.js +107 -0
- package/dist/capabilities/events.js.map +1 -0
- package/dist/capabilities/files.d.ts +3 -0
- package/dist/capabilities/files.d.ts.map +1 -0
- package/dist/capabilities/files.js +100 -0
- package/dist/capabilities/files.js.map +1 -0
- package/dist/capabilities/helpers.d.ts +27 -0
- package/dist/capabilities/helpers.d.ts.map +1 -0
- package/dist/capabilities/helpers.js +63 -0
- package/dist/capabilities/helpers.js.map +1 -0
- package/dist/capabilities/import-export.d.ts +3 -0
- package/dist/capabilities/import-export.d.ts.map +1 -0
- package/dist/capabilities/import-export.js +267 -0
- package/dist/capabilities/import-export.js.map +1 -0
- package/dist/capabilities/index.d.ts +16 -0
- package/dist/capabilities/index.d.ts.map +1 -0
- package/dist/capabilities/index.js +71 -0
- package/dist/capabilities/index.js.map +1 -0
- package/dist/capabilities/jobs.d.ts +3 -0
- package/dist/capabilities/jobs.d.ts.map +1 -0
- package/dist/capabilities/jobs.js +117 -0
- package/dist/capabilities/jobs.js.map +1 -0
- package/dist/capabilities/notifications.d.ts +3 -0
- package/dist/capabilities/notifications.d.ts.map +1 -0
- package/dist/capabilities/notifications.js +113 -0
- package/dist/capabilities/notifications.js.map +1 -0
- package/dist/capabilities/search.d.ts +3 -0
- package/dist/capabilities/search.d.ts.map +1 -0
- package/dist/capabilities/search.js +121 -0
- package/dist/capabilities/search.js.map +1 -0
- package/dist/capabilities/settings.d.ts +3 -0
- package/dist/capabilities/settings.d.ts.map +1 -0
- package/dist/capabilities/settings.js +75 -0
- package/dist/capabilities/settings.js.map +1 -0
- package/dist/capabilities/types.d.ts +16 -0
- package/dist/capabilities/types.d.ts.map +1 -0
- package/dist/capabilities/types.js +3 -0
- package/dist/capabilities/types.js.map +1 -0
- package/dist/capabilities/webhooks.d.ts +3 -0
- package/dist/capabilities/webhooks.d.ts.map +1 -0
- package/dist/capabilities/webhooks.js +117 -0
- package/dist/capabilities/webhooks.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +60 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/add.d.ts +3 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +88 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +94 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/generate.d.ts +3 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +41 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/inspect.d.ts +3 -0
- package/dist/commands/inspect.d.ts.map +1 -0
- package/dist/commands/inspect.js +64 -0
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/new.d.ts +18 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +149 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/shared.d.ts +14 -0
- package/dist/commands/shared.d.ts.map +1 -0
- package/dist/commands/shared.js +36 -0
- package/dist/commands/shared.js.map +1 -0
- package/dist/commands/skill.d.ts +3 -0
- package/dist/commands/skill.d.ts.map +1 -0
- package/dist/commands/skill.js +59 -0
- package/dist/commands/skill.js.map +1 -0
- package/dist/commands/stack.d.ts +13 -0
- package/dist/commands/stack.d.ts.map +1 -0
- package/dist/commands/stack.js +192 -0
- package/dist/commands/stack.js.map +1 -0
- package/dist/generators/api.d.ts +16 -0
- package/dist/generators/api.d.ts.map +1 -0
- package/dist/generators/api.js +375 -0
- package/dist/generators/api.js.map +1 -0
- package/dist/generators/lwd.d.ts +12 -0
- package/dist/generators/lwd.d.ts.map +1 -0
- package/dist/generators/lwd.js +143 -0
- package/dist/generators/lwd.js.map +1 -0
- package/dist/generators/mobile.d.ts +16 -0
- package/dist/generators/mobile.d.ts.map +1 -0
- package/dist/generators/mobile.js +180 -0
- package/dist/generators/mobile.js.map +1 -0
- package/dist/generators/platform-package.d.ts +18 -0
- package/dist/generators/platform-package.d.ts.map +1 -0
- package/dist/generators/platform-package.js +568 -0
- package/dist/generators/platform-package.js.map +1 -0
- package/dist/generators/root.d.ts +15 -0
- package/dist/generators/root.d.ts.map +1 -0
- package/dist/generators/root.js +249 -0
- package/dist/generators/root.js.map +1 -0
- package/dist/generators/sdk.d.ts +14 -0
- package/dist/generators/sdk.d.ts.map +1 -0
- package/dist/generators/sdk.js +137 -0
- package/dist/generators/sdk.js.map +1 -0
- package/dist/generators/web.d.ts +15 -0
- package/dist/generators/web.d.ts.map +1 -0
- package/dist/generators/web.js +203 -0
- package/dist/generators/web.js.map +1 -0
- package/dist/generators/worker.d.ts +13 -0
- package/dist/generators/worker.d.ts.map +1 -0
- package/dist/generators/worker.js +148 -0
- package/dist/generators/worker.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/project/context.d.ts +24 -0
- package/dist/project/context.d.ts.map +1 -0
- package/dist/project/context.js +63 -0
- package/dist/project/context.js.map +1 -0
- package/dist/project/manifest.d.ts +26 -0
- package/dist/project/manifest.d.ts.map +1 -0
- package/dist/project/manifest.js +73 -0
- package/dist/project/manifest.js.map +1 -0
- package/dist/project/paths.d.ts +25 -0
- package/dist/project/paths.d.ts.map +1 -0
- package/dist/project/paths.js +51 -0
- package/dist/project/paths.js.map +1 -0
- package/dist/project/plan.d.ts +42 -0
- package/dist/project/plan.d.ts.map +1 -0
- package/dist/project/plan.js +251 -0
- package/dist/project/plan.js.map +1 -0
- package/dist/prompts/index.d.ts +24 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +111 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/recipes/index.d.ts +25 -0
- package/dist/recipes/index.d.ts.map +1 -0
- package/dist/recipes/index.js +77 -0
- package/dist/recipes/index.js.map +1 -0
- package/dist/stack/infer.d.ts +4 -0
- package/dist/stack/infer.d.ts.map +1 -0
- package/dist/stack/infer.js +165 -0
- package/dist/stack/infer.js.map +1 -0
- package/dist/stack/lwd.d.ts +24 -0
- package/dist/stack/lwd.d.ts.map +1 -0
- package/dist/stack/lwd.js +62 -0
- package/dist/stack/lwd.js.map +1 -0
- package/dist/stack/lwdtoml.d.ts +38 -0
- package/dist/stack/lwdtoml.d.ts.map +1 -0
- package/dist/stack/lwdtoml.js +57 -0
- package/dist/stack/lwdtoml.js.map +1 -0
- package/dist/stack/manifest.d.ts +15 -0
- package/dist/stack/manifest.d.ts.map +1 -0
- package/dist/stack/manifest.js +86 -0
- package/dist/stack/manifest.js.map +1 -0
- package/dist/stack/types.d.ts +48 -0
- package/dist/stack/types.d.ts.map +1 -0
- package/dist/stack/types.js +5 -0
- package/dist/stack/types.js.map +1 -0
- package/dist/stack/wire.d.ts +40 -0
- package/dist/stack/wire.d.ts.map +1 -0
- package/dist/stack/wire.js +192 -0
- package/dist/stack/wire.js.map +1 -0
- package/dist/types.d.ts +114 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/fs.d.ts +13 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +64 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/git.d.ts +17 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +49 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/logger.d.ts +16 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +21 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/shell.d.ts +21 -0
- package/dist/utils/shell.d.ts.map +1 -0
- package/dist/utils/shell.js +40 -0
- package/dist/utils/shell.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +6 -0
- package/dist/version.js.map +1 -0
- package/package.json +40 -0
- package/skills/obh-add-events/SKILL.md +26 -0
- package/skills/obh-generate-audit-rules/SKILL.md +26 -0
- package/skills/obh-inspect-project/SKILL.md +26 -0
- package/skills/obh-lwd-manifest/SKILL.md +26 -0
- package/skills/obh-retrofit-files/SKILL.md +24 -0
- package/skills/obh-retrofit-jobs/SKILL.md +26 -0
- package/skills/obh-sdk-extraction/SKILL.md +24 -0
- package/skills/obh-settings-migration/SKILL.md +24 -0
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.addPlatformPackageProject = addPlatformPackageProject;
|
|
4
|
+
const pascal = (name) => name
|
|
5
|
+
.split(/[-_]/)
|
|
6
|
+
.filter(Boolean)
|
|
7
|
+
.map((s) => s.charAt(0).toUpperCase() + s.slice(1))
|
|
8
|
+
.join("");
|
|
9
|
+
const snake = (name) => name.replace(/-/g, "_");
|
|
10
|
+
/**
|
|
11
|
+
* Generates a complete OBH platform-primitive repo (the qtool template): a pnpm
|
|
12
|
+
* monorepo publishing @obh/<name>, CommonJS + strict, Postgres-native with the
|
|
13
|
+
* standard pgAdapter / logger / ids / migrations kernel, a real record-store
|
|
14
|
+
* client over a platform.<name> table, tests, and CI. Optionally an admin/worker
|
|
15
|
+
* daemon. This is the single source that keeps new primitives consistent.
|
|
16
|
+
*/
|
|
17
|
+
function addPlatformPackageProject(plan, opts) {
|
|
18
|
+
const { name, scope, daemon } = opts;
|
|
19
|
+
const P = pascal(name);
|
|
20
|
+
const table = `platform.${snake(name)}`;
|
|
21
|
+
const pkgDir = `packages/${name}`;
|
|
22
|
+
const idPrefix = snake(name).slice(0, 6);
|
|
23
|
+
// ---- repo root ----
|
|
24
|
+
plan.create("package.json", rootPackageJson(name), "root workspace package.json");
|
|
25
|
+
plan.create("pnpm-workspace.yaml", "packages:\n - packages/*\n - apps/*\n", "pnpm workspace");
|
|
26
|
+
plan.create("tsconfig.base.json", TSCONFIG_BASE, "shared TypeScript base (CommonJS, strict)");
|
|
27
|
+
plan.create(".gitignore", "node_modules/\ndist/\ncoverage/\n*.log\n.env\n", "gitignore");
|
|
28
|
+
plan.create("LICENSE", mitLicense(), "MIT license");
|
|
29
|
+
plan.create("README.md", readme(name, scope, P), "readme");
|
|
30
|
+
plan.create(".github/workflows/ci.yml", ci(name), "CI (postgres:16, build/typecheck/test)");
|
|
31
|
+
// ---- packages/<name> ----
|
|
32
|
+
plan.create(`${pkgDir}/package.json`, pkgPackageJson(name, scope), "package.json");
|
|
33
|
+
plan.create(`${pkgDir}/tsconfig.json`, PKG_TSCONFIG, "package tsconfig");
|
|
34
|
+
plan.create(`${pkgDir}/vitest.config.ts`, VITEST, "vitest config");
|
|
35
|
+
plan.create(`${pkgDir}/src/index.ts`, indexTs(P), "public API surface");
|
|
36
|
+
plan.create(`${pkgDir}/src/db.ts`, DB_TS, "db types");
|
|
37
|
+
plan.create(`${pkgDir}/src/adapters/pg.ts`, PG_ADAPTER, "pgAdapter");
|
|
38
|
+
plan.create(`${pkgDir}/src/logger.ts`, LOGGER, "structured logger");
|
|
39
|
+
plan.create(`${pkgDir}/src/ids.ts`, IDS, "id generator");
|
|
40
|
+
plan.create(`${pkgDir}/src/registry.ts`, registryTs(P), "definition registry");
|
|
41
|
+
plan.create(`${pkgDir}/src/client.ts`, clientTs(P, table, idPrefix), "record-store client");
|
|
42
|
+
plan.create(`${pkgDir}/src/migrations.ts`, migrationsTs(name, table), "migration runner");
|
|
43
|
+
plan.create(`${pkgDir}/src/migrations/0001_init.sql`, initSql(table), "initial migration");
|
|
44
|
+
plan.create(`${pkgDir}/src/__tests__/ids.test.ts`, IDS_TEST, "ids unit test");
|
|
45
|
+
plan.create(`${pkgDir}/src/__tests__/registry.test.ts`, registryTest(P), "registry unit test");
|
|
46
|
+
// ---- optional daemon ----
|
|
47
|
+
if (daemon) {
|
|
48
|
+
const appDir = `apps/${name}d`;
|
|
49
|
+
plan.create(`${appDir}/package.json`, daemonPackageJson(name, scope), "daemon package.json");
|
|
50
|
+
plan.create(`${appDir}/tsconfig.json`, PKG_TSCONFIG, "daemon tsconfig");
|
|
51
|
+
plan.create(`${appDir}/src/config.ts`, DAEMON_CONFIG, "daemon config");
|
|
52
|
+
plan.create(`${appDir}/src/migrate.ts`, daemonMigrate(scope, name), "migrate binary");
|
|
53
|
+
plan.create(`${appDir}/src/main.ts`, daemonMain(scope, name), "daemon entry (migrate + heartbeat)");
|
|
54
|
+
}
|
|
55
|
+
plan.nextStep(`cd ${name} && pnpm install && pnpm build`);
|
|
56
|
+
if (daemon)
|
|
57
|
+
plan.nextStep(`Apply schema: DATABASE_URL=… pnpm --filter ${scope}/${name}d migrate`);
|
|
58
|
+
plan.nextStep(`Rename ${P}Record / create${P}Client in packages/${name}/src to your domain.`);
|
|
59
|
+
}
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
function rootPackageJson(name) {
|
|
62
|
+
return (JSON.stringify({
|
|
63
|
+
name,
|
|
64
|
+
private: true,
|
|
65
|
+
version: "0.1.0",
|
|
66
|
+
packageManager: "pnpm@9.7.0",
|
|
67
|
+
engines: { node: ">=20" },
|
|
68
|
+
scripts: { build: "pnpm -r build", typecheck: "pnpm -r typecheck", test: "pnpm -r test" },
|
|
69
|
+
devDependencies: { typescript: "^5.5.4" },
|
|
70
|
+
}, null, 2) + "\n");
|
|
71
|
+
}
|
|
72
|
+
const TSCONFIG_BASE = `{
|
|
73
|
+
"compilerOptions": {
|
|
74
|
+
"target": "ES2022",
|
|
75
|
+
"module": "commonjs",
|
|
76
|
+
"moduleResolution": "node",
|
|
77
|
+
"lib": ["ES2022"],
|
|
78
|
+
"declaration": true,
|
|
79
|
+
"declarationMap": true,
|
|
80
|
+
"sourceMap": true,
|
|
81
|
+
"strict": true,
|
|
82
|
+
"esModuleInterop": true,
|
|
83
|
+
"skipLibCheck": true,
|
|
84
|
+
"forceConsistentCasingInFileNames": true,
|
|
85
|
+
"resolveJsonModule": true,
|
|
86
|
+
"types": ["node"]
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
`;
|
|
90
|
+
const PKG_TSCONFIG = `{
|
|
91
|
+
"extends": "../../tsconfig.base.json",
|
|
92
|
+
"compilerOptions": { "rootDir": "src", "outDir": "dist", "composite": true },
|
|
93
|
+
"include": ["src"],
|
|
94
|
+
"exclude": ["dist", "node_modules", "src/**/*.test.ts"]
|
|
95
|
+
}
|
|
96
|
+
`;
|
|
97
|
+
const VITEST = `import { defineConfig } from "vitest/config"
|
|
98
|
+
|
|
99
|
+
export default defineConfig({ test: { include: ["src/**/*.test.ts"] } })
|
|
100
|
+
`;
|
|
101
|
+
function pkgPackageJson(name, scope) {
|
|
102
|
+
return (JSON.stringify({
|
|
103
|
+
name: `${scope}/${name}`,
|
|
104
|
+
version: "0.1.0",
|
|
105
|
+
license: "MIT",
|
|
106
|
+
main: "dist/index.js",
|
|
107
|
+
types: "dist/index.d.ts",
|
|
108
|
+
files: ["dist", "src/migrations"],
|
|
109
|
+
scripts: {
|
|
110
|
+
build: "tsc -p tsconfig.json",
|
|
111
|
+
typecheck: "tsc -p tsconfig.json --noEmit",
|
|
112
|
+
test: "vitest run",
|
|
113
|
+
},
|
|
114
|
+
devDependencies: {
|
|
115
|
+
"@types/node": "^20.14.0",
|
|
116
|
+
"@types/pg": "^8.11.6",
|
|
117
|
+
pg: "^8.12.0",
|
|
118
|
+
typescript: "^5.5.4",
|
|
119
|
+
vitest: "^2.0.5",
|
|
120
|
+
},
|
|
121
|
+
}, null, 2) + "\n");
|
|
122
|
+
}
|
|
123
|
+
function indexTs(P) {
|
|
124
|
+
return `// Public surface of the package. Keep this small and boring.
|
|
125
|
+
export type { Db, TransactionalDb, QueryResult } from "./db"
|
|
126
|
+
export { pgAdapter } from "./adapters/pg"
|
|
127
|
+
export { createLogger } from "./logger"
|
|
128
|
+
export type { Logger, LogLevel, LogFields } from "./logger"
|
|
129
|
+
export { newId } from "./ids"
|
|
130
|
+
|
|
131
|
+
export { define${P}, create${P}Registry } from "./registry"
|
|
132
|
+
export type { ${P}Definition, ${P}Registry } from "./registry"
|
|
133
|
+
|
|
134
|
+
export { create${P}Client } from "./client"
|
|
135
|
+
export type { ${P}Client, ${P}Record } from "./client"
|
|
136
|
+
|
|
137
|
+
export { runMigrations, migrations, INIT_SQL } from "./migrations"
|
|
138
|
+
export type { Migration } from "./migrations"
|
|
139
|
+
`;
|
|
140
|
+
}
|
|
141
|
+
const DB_TS = `export interface QueryResult<T> {
|
|
142
|
+
rows: T[]
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface Db {
|
|
146
|
+
query<T>(sql: string, params?: unknown[]): Promise<QueryResult<T>>
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface TransactionalDb extends Db {
|
|
150
|
+
transaction<T>(fn: (tx: Db) => Promise<T>): Promise<T>
|
|
151
|
+
}
|
|
152
|
+
`;
|
|
153
|
+
const PG_ADAPTER = `import type { Db, QueryResult, TransactionalDb } from "../db"
|
|
154
|
+
|
|
155
|
+
type PgQueryable = { query(text: string, params?: unknown[]): Promise<{ rows: unknown[] }> }
|
|
156
|
+
type PgClient = PgQueryable & { release: () => void }
|
|
157
|
+
type PgPool = PgQueryable & { connect: () => Promise<PgClient> }
|
|
158
|
+
|
|
159
|
+
/** Wrap a node-postgres Pool as a TransactionalDb. */
|
|
160
|
+
export function pgAdapter(pool: PgPool): TransactionalDb {
|
|
161
|
+
const query = async <T>(sql: string, params?: unknown[]): Promise<QueryResult<T>> => {
|
|
162
|
+
const res = await pool.query(sql, params)
|
|
163
|
+
return { rows: res.rows as T[] }
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
query,
|
|
167
|
+
async transaction<T>(fn: (tx: Db) => Promise<T>): Promise<T> {
|
|
168
|
+
const client = await pool.connect()
|
|
169
|
+
const tx: Db = {
|
|
170
|
+
async query<U>(sql: string, params?: unknown[]): Promise<QueryResult<U>> {
|
|
171
|
+
const res = await client.query(sql, params)
|
|
172
|
+
return { rows: res.rows as U[] }
|
|
173
|
+
},
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
await client.query("begin")
|
|
177
|
+
const result = await fn(tx)
|
|
178
|
+
await client.query("commit")
|
|
179
|
+
return result
|
|
180
|
+
} catch (err) {
|
|
181
|
+
await client.query("rollback")
|
|
182
|
+
throw err
|
|
183
|
+
} finally {
|
|
184
|
+
client.release()
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
`;
|
|
190
|
+
const LOGGER = `export type LogLevel = "debug" | "info" | "warn" | "error"
|
|
191
|
+
export type LogFields = Record<string, unknown>
|
|
192
|
+
|
|
193
|
+
export interface Logger {
|
|
194
|
+
debug(msg: string, fields?: LogFields): void
|
|
195
|
+
info(msg: string, fields?: LogFields): void
|
|
196
|
+
warn(msg: string, fields?: LogFields): void
|
|
197
|
+
error(msg: string, fields?: LogFields): void
|
|
198
|
+
child(base: LogFields): Logger
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const ORDER: Record<LogLevel, number> = { debug: 10, info: 20, warn: 30, error: 40 }
|
|
202
|
+
|
|
203
|
+
export function createLogger(level: LogLevel = "info", base: LogFields = {}): Logger {
|
|
204
|
+
const at = (lvl: LogLevel, msg: string, fields?: LogFields): void => {
|
|
205
|
+
if (ORDER[lvl] < ORDER[level]) return
|
|
206
|
+
console.log(JSON.stringify({ level: lvl, msg, ...base, ...fields }))
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
debug: (m, f) => at("debug", m, f),
|
|
210
|
+
info: (m, f) => at("info", m, f),
|
|
211
|
+
warn: (m, f) => at("warn", m, f),
|
|
212
|
+
error: (m, f) => at("error", m, f),
|
|
213
|
+
child: (b) => createLogger(level, { ...base, ...b }),
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
`;
|
|
217
|
+
const IDS = `import { randomUUID } from "node:crypto"
|
|
218
|
+
|
|
219
|
+
/** Generate a prefixed, sortable-enough id, e.g. newId("wid") -> "wid_<uuid>". */
|
|
220
|
+
export function newId(prefix: string): string {
|
|
221
|
+
return prefix + "_" + randomUUID()
|
|
222
|
+
}
|
|
223
|
+
`;
|
|
224
|
+
function registryTs(P) {
|
|
225
|
+
return `export interface ${P}Definition {
|
|
226
|
+
name: string
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export interface ${P}Registry {
|
|
230
|
+
register(def: ${P}Definition): void
|
|
231
|
+
get(name: string): ${P}Definition | undefined
|
|
232
|
+
list(): ${P}Definition[]
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function define${P}(def: ${P}Definition): ${P}Definition {
|
|
236
|
+
return def
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function create${P}Registry(): ${P}Registry {
|
|
240
|
+
const defs = new Map<string, ${P}Definition>()
|
|
241
|
+
return {
|
|
242
|
+
register: (def) => void defs.set(def.name, def),
|
|
243
|
+
get: (name) => defs.get(name),
|
|
244
|
+
list: () => [...defs.values()],
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
`;
|
|
248
|
+
}
|
|
249
|
+
function clientTs(P, table, idPrefix) {
|
|
250
|
+
return `import type { Db } from "./db"
|
|
251
|
+
import { newId } from "./ids"
|
|
252
|
+
|
|
253
|
+
export interface ${P}Record {
|
|
254
|
+
id: string
|
|
255
|
+
workspaceId: string
|
|
256
|
+
kind: string
|
|
257
|
+
key: string
|
|
258
|
+
value: unknown
|
|
259
|
+
createdAt: string
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
interface Row {
|
|
263
|
+
id: string
|
|
264
|
+
workspace_id: string
|
|
265
|
+
kind: string
|
|
266
|
+
key: string
|
|
267
|
+
value: unknown
|
|
268
|
+
created_at: Date
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const toRecord = (r: Row): ${P}Record => ({
|
|
272
|
+
id: r.id,
|
|
273
|
+
workspaceId: r.workspace_id,
|
|
274
|
+
kind: r.kind,
|
|
275
|
+
key: r.key,
|
|
276
|
+
value: r.value,
|
|
277
|
+
createdAt: r.created_at.toISOString(),
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
export interface ${P}Client {
|
|
281
|
+
put(input: { workspaceId: string; kind: string; key: string; value: unknown }): Promise<${P}Record>
|
|
282
|
+
get(workspaceId: string, kind: string, key: string): Promise<${P}Record | null>
|
|
283
|
+
list(workspaceId: string, kind: string): Promise<${P}Record[]>
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** A boring, workspace-scoped record store. Reshape to your primitive's domain. */
|
|
287
|
+
export function create${P}Client(opts: { db: Db }): ${P}Client {
|
|
288
|
+
const { db } = opts
|
|
289
|
+
return {
|
|
290
|
+
async put(input) {
|
|
291
|
+
const { rows } = await db.query<Row>(
|
|
292
|
+
\`insert into ${table} (id, workspace_id, kind, key, value)
|
|
293
|
+
values ($1, $2, $3, $4, $5::jsonb)
|
|
294
|
+
on conflict (workspace_id, kind, key) do update set value = excluded.value
|
|
295
|
+
returning *\`,
|
|
296
|
+
[newId("${idPrefix}"), input.workspaceId, input.kind, input.key, JSON.stringify(input.value)],
|
|
297
|
+
)
|
|
298
|
+
return toRecord(rows[0])
|
|
299
|
+
},
|
|
300
|
+
async get(workspaceId, kind, key) {
|
|
301
|
+
const { rows } = await db.query<Row>(
|
|
302
|
+
\`select * from ${table} where workspace_id = $1 and kind = $2 and key = $3\`,
|
|
303
|
+
[workspaceId, kind, key],
|
|
304
|
+
)
|
|
305
|
+
return rows[0] ? toRecord(rows[0]) : null
|
|
306
|
+
},
|
|
307
|
+
async list(workspaceId, kind) {
|
|
308
|
+
const { rows } = await db.query<Row>(
|
|
309
|
+
\`select * from ${table} where workspace_id = $1 and kind = $2 order by created_at desc\`,
|
|
310
|
+
[workspaceId, kind],
|
|
311
|
+
)
|
|
312
|
+
return rows.map(toRecord)
|
|
313
|
+
},
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
`;
|
|
317
|
+
}
|
|
318
|
+
function migrationsTs(name, table) {
|
|
319
|
+
const tracking = `platform.${snake(name)}_migrations`;
|
|
320
|
+
return `import type { Db } from "./db"
|
|
321
|
+
|
|
322
|
+
export interface Migration {
|
|
323
|
+
name: string
|
|
324
|
+
sql: string
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export const INIT_SQL = \`${initSql(table)}\`
|
|
328
|
+
|
|
329
|
+
export const migrations: Migration[] = [{ name: "0001_init", sql: INIT_SQL }]
|
|
330
|
+
|
|
331
|
+
/** Idempotently apply all migrations. */
|
|
332
|
+
export async function runMigrations(db: Db): Promise<void> {
|
|
333
|
+
await db.query("create schema if not exists platform")
|
|
334
|
+
await db.query(
|
|
335
|
+
"create table if not exists ${tracking} (name text primary key, applied_at timestamptz not null default now())",
|
|
336
|
+
)
|
|
337
|
+
for (const m of migrations) {
|
|
338
|
+
const { rows } = await db.query<{ name: string }>("select name from ${tracking} where name = $1", [m.name])
|
|
339
|
+
if (rows.length) continue
|
|
340
|
+
await db.query(m.sql)
|
|
341
|
+
await db.query("insert into ${tracking} (name) values ($1)", [m.name])
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
`;
|
|
345
|
+
}
|
|
346
|
+
function initSql(table) {
|
|
347
|
+
return `create schema if not exists platform;
|
|
348
|
+
|
|
349
|
+
create table if not exists ${table} (
|
|
350
|
+
id text primary key,
|
|
351
|
+
workspace_id text not null,
|
|
352
|
+
kind text not null,
|
|
353
|
+
key text not null,
|
|
354
|
+
value jsonb not null default '{}'::jsonb,
|
|
355
|
+
created_at timestamptz not null default now(),
|
|
356
|
+
unique (workspace_id, kind, key)
|
|
357
|
+
);
|
|
358
|
+
`;
|
|
359
|
+
}
|
|
360
|
+
const IDS_TEST = `import { describe, expect, it } from "vitest"
|
|
361
|
+
import { newId } from "../ids"
|
|
362
|
+
|
|
363
|
+
describe("newId", () => {
|
|
364
|
+
it("prefixes and is unique", () => {
|
|
365
|
+
const a = newId("t")
|
|
366
|
+
const b = newId("t")
|
|
367
|
+
expect(a.startsWith("t_")).toBe(true)
|
|
368
|
+
expect(a).not.toBe(b)
|
|
369
|
+
})
|
|
370
|
+
})
|
|
371
|
+
`;
|
|
372
|
+
function registryTest(P) {
|
|
373
|
+
return `import { describe, expect, it } from "vitest"
|
|
374
|
+
import { create${P}Registry, define${P} } from "../registry"
|
|
375
|
+
|
|
376
|
+
describe("registry", () => {
|
|
377
|
+
it("registers and lists definitions", () => {
|
|
378
|
+
const registry = create${P}Registry()
|
|
379
|
+
registry.register(define${P}({ name: "example" }))
|
|
380
|
+
expect(registry.get("example")?.name).toBe("example")
|
|
381
|
+
expect(registry.list()).toHaveLength(1)
|
|
382
|
+
})
|
|
383
|
+
})
|
|
384
|
+
`;
|
|
385
|
+
}
|
|
386
|
+
// ---- daemon ----
|
|
387
|
+
function daemonPackageJson(name, scope) {
|
|
388
|
+
return (JSON.stringify({
|
|
389
|
+
name: `${scope}/${name}d`,
|
|
390
|
+
version: "0.1.0",
|
|
391
|
+
private: true,
|
|
392
|
+
bin: { [`obh-${name}d`]: "dist/main.js" },
|
|
393
|
+
scripts: {
|
|
394
|
+
build: "tsc -p tsconfig.json",
|
|
395
|
+
typecheck: "tsc -p tsconfig.json --noEmit",
|
|
396
|
+
start: "node dist/main.js",
|
|
397
|
+
migrate: "node dist/migrate.js",
|
|
398
|
+
test: "vitest run --passWithNoTests",
|
|
399
|
+
},
|
|
400
|
+
dependencies: { [`${scope}/${name}`]: "workspace:*", pg: "^8.12.0" },
|
|
401
|
+
devDependencies: {
|
|
402
|
+
"@types/node": "^20.14.0",
|
|
403
|
+
"@types/pg": "^8.11.6",
|
|
404
|
+
typescript: "^5.5.4",
|
|
405
|
+
vitest: "^2.0.5",
|
|
406
|
+
},
|
|
407
|
+
}, null, 2) + "\n");
|
|
408
|
+
}
|
|
409
|
+
const DAEMON_CONFIG = `export function loadConfig(): { databaseUrl: string; logLevel: string; pollIntervalMs: number } {
|
|
410
|
+
const databaseUrl = process.env.DATABASE_URL
|
|
411
|
+
if (!databaseUrl) throw new Error("DATABASE_URL is not set")
|
|
412
|
+
return {
|
|
413
|
+
databaseUrl,
|
|
414
|
+
logLevel: process.env.LOG_LEVEL ?? "info",
|
|
415
|
+
pollIntervalMs: Number(process.env.POLL_INTERVAL_MS) || 1000,
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
`;
|
|
419
|
+
function daemonMigrate(scope, name) {
|
|
420
|
+
return `#!/usr/bin/env node
|
|
421
|
+
import { pgAdapter, runMigrations } from "${scope}/${name}"
|
|
422
|
+
import { Pool } from "pg"
|
|
423
|
+
import { loadConfig } from "./config"
|
|
424
|
+
|
|
425
|
+
async function main(): Promise<void> {
|
|
426
|
+
const cfg = loadConfig()
|
|
427
|
+
const pool = new Pool({ connectionString: cfg.databaseUrl })
|
|
428
|
+
try {
|
|
429
|
+
await runMigrations(pgAdapter(pool))
|
|
430
|
+
console.log("migrations applied")
|
|
431
|
+
} finally {
|
|
432
|
+
await pool.end()
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
main().catch((err) => {
|
|
437
|
+
console.error(err)
|
|
438
|
+
process.exit(1)
|
|
439
|
+
})
|
|
440
|
+
`;
|
|
441
|
+
}
|
|
442
|
+
function daemonMain(scope, name) {
|
|
443
|
+
return `#!/usr/bin/env node
|
|
444
|
+
import { createLogger, pgAdapter, runMigrations } from "${scope}/${name}"
|
|
445
|
+
import { Pool } from "pg"
|
|
446
|
+
import { loadConfig } from "./config"
|
|
447
|
+
|
|
448
|
+
async function main(): Promise<void> {
|
|
449
|
+
const cfg = loadConfig()
|
|
450
|
+
const log = createLogger(cfg.logLevel as "info")
|
|
451
|
+
const pool = new Pool({ connectionString: cfg.databaseUrl })
|
|
452
|
+
await runMigrations(pgAdapter(pool))
|
|
453
|
+
log.info("daemon started")
|
|
454
|
+
|
|
455
|
+
let running = true
|
|
456
|
+
const tick = async (): Promise<void> => {
|
|
457
|
+
// Add background work here (sweeps, retries, rollups) as the primitive grows.
|
|
458
|
+
}
|
|
459
|
+
const timer = setInterval(() => void tick(), cfg.pollIntervalMs)
|
|
460
|
+
|
|
461
|
+
const shutdown = async (): Promise<void> => {
|
|
462
|
+
if (!running) return
|
|
463
|
+
running = false
|
|
464
|
+
clearInterval(timer)
|
|
465
|
+
await pool.end()
|
|
466
|
+
process.exit(0)
|
|
467
|
+
}
|
|
468
|
+
process.on("SIGTERM", () => void shutdown())
|
|
469
|
+
process.on("SIGINT", () => void shutdown())
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
main().catch((err) => {
|
|
473
|
+
console.error(err)
|
|
474
|
+
process.exit(1)
|
|
475
|
+
})
|
|
476
|
+
`;
|
|
477
|
+
}
|
|
478
|
+
function ci(name) {
|
|
479
|
+
return `name: CI
|
|
480
|
+
|
|
481
|
+
on:
|
|
482
|
+
push:
|
|
483
|
+
branches: [main]
|
|
484
|
+
pull_request:
|
|
485
|
+
|
|
486
|
+
jobs:
|
|
487
|
+
build-test:
|
|
488
|
+
runs-on: ubuntu-latest
|
|
489
|
+
services:
|
|
490
|
+
postgres:
|
|
491
|
+
image: postgres:16
|
|
492
|
+
env:
|
|
493
|
+
POSTGRES_USER: postgres
|
|
494
|
+
POSTGRES_PASSWORD: postgres
|
|
495
|
+
POSTGRES_DB: ${snake(name)}_test
|
|
496
|
+
ports:
|
|
497
|
+
- 5432:5432
|
|
498
|
+
options: >-
|
|
499
|
+
--health-cmd "pg_isready -U postgres"
|
|
500
|
+
--health-interval 10s
|
|
501
|
+
--health-timeout 5s
|
|
502
|
+
--health-retries 5
|
|
503
|
+
env:
|
|
504
|
+
DATABASE_URL: postgres://postgres:postgres@localhost:5432/${snake(name)}_test
|
|
505
|
+
steps:
|
|
506
|
+
- uses: actions/checkout@v4
|
|
507
|
+
- uses: pnpm/action-setup@v4
|
|
508
|
+
with:
|
|
509
|
+
version: 9
|
|
510
|
+
- uses: actions/setup-node@v4
|
|
511
|
+
with:
|
|
512
|
+
node-version: 20
|
|
513
|
+
cache: pnpm
|
|
514
|
+
- run: pnpm install --no-frozen-lockfile
|
|
515
|
+
- run: pnpm -r build
|
|
516
|
+
- run: pnpm -r test
|
|
517
|
+
`;
|
|
518
|
+
}
|
|
519
|
+
function readme(name, scope, P) {
|
|
520
|
+
return `# ${name}
|
|
521
|
+
|
|
522
|
+
\`${scope}/${name}\` — an OBH platform primitive, scaffolded by [OBH Forge](https://github.com/EObrien60/forge).
|
|
523
|
+
|
|
524
|
+
Postgres-backed, framework-agnostic, standalone. Public API:
|
|
525
|
+
|
|
526
|
+
\`\`\`ts
|
|
527
|
+
import { create${P}Client, pgAdapter, runMigrations } from "${scope}/${name}"
|
|
528
|
+
import { Pool } from "pg"
|
|
529
|
+
|
|
530
|
+
const pool = new Pool({ connectionString: process.env.DATABASE_URL })
|
|
531
|
+
await runMigrations(pgAdapter(pool))
|
|
532
|
+
|
|
533
|
+
const client = create${P}Client({ db: pgAdapter(pool) })
|
|
534
|
+
await client.put({ workspaceId: "ws1", kind: "example", key: "k", value: { any: "json" } })
|
|
535
|
+
\`\`\`
|
|
536
|
+
|
|
537
|
+
Tables live under the shared \`platform\` schema. Reshape \`${P}Record\` /
|
|
538
|
+
\`create${P}Client\` to your primitive's real domain — the record store is a
|
|
539
|
+
starting point, not a straitjacket.
|
|
540
|
+
|
|
541
|
+
## Develop
|
|
542
|
+
|
|
543
|
+
\`\`\`bash
|
|
544
|
+
pnpm install
|
|
545
|
+
pnpm build
|
|
546
|
+
pnpm test # unit + Postgres integration (set DATABASE_URL)
|
|
547
|
+
\`\`\`
|
|
548
|
+
`;
|
|
549
|
+
}
|
|
550
|
+
function mitLicense() {
|
|
551
|
+
return `MIT License
|
|
552
|
+
|
|
553
|
+
Copyright (c) 2026 OBH Software
|
|
554
|
+
|
|
555
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
556
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
557
|
+
in the Software without restriction, including without limitation the rights
|
|
558
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
559
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
560
|
+
furnished to do so, subject to the following conditions:
|
|
561
|
+
|
|
562
|
+
The above copyright notice and this permission notice shall be included in all
|
|
563
|
+
copies or substantial portions of the Software.
|
|
564
|
+
|
|
565
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
|
|
566
|
+
`;
|
|
567
|
+
}
|
|
568
|
+
//# sourceMappingURL=platform-package.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-package.js","sourceRoot":"","sources":["../../src/generators/platform-package.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Topology } from "../types";
|
|
2
|
+
import type { Plan } from "../project/plan";
|
|
3
|
+
export interface SkeletonOptions {
|
|
4
|
+
name: string;
|
|
5
|
+
topology: Topology;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Adds the project-level skeleton shared by every recipe: workspace config,
|
|
9
|
+
* base tsconfig, migration runner, CI, and docs. Everything here is a safe
|
|
10
|
+
* "create" — re-running never clobbers edited files.
|
|
11
|
+
*/
|
|
12
|
+
export declare function addProjectSkeleton(plan: Plan, opts: SkeletonOptions): void;
|
|
13
|
+
export declare function envBase(name: string): string;
|
|
14
|
+
export declare function ciYaml(name: string): string;
|
|
15
|
+
//# sourceMappingURL=root.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../../src/generators/root.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,QAAQ,EAAE,MAAM,UAAU,CAAA;AACxD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAE3C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,QAAQ,CAAA;CACnB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,eAAe,GAAG,IAAI,CAiF1E;AA2ED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM5C;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA4C3C"}
|