Skip to content

Commit d4160e1

Browse files
committed
feat: Enhance local project support and artifact handling
- Updated ROADMAP.md to reflect completion of local project environment paths and Drift fixes. - Modified objectstack.config.ts to support local mode with specific environment variables and artifact compilation. - Added new Project Artifact documentation to clarify the response shape of the artifact API. - Removed outdated plugin-bi example and its associated files. - Improved metadata plugin to load from local artifact files and handle metadata registration. - Updated dev command to compile objectstack.config.ts when necessary and streamline local development setup.
1 parent 6531bec commit d4160e1

13 files changed

Lines changed: 448 additions & 525 deletions

File tree

ROADMAP.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,9 @@ Ordered by dependency. Items higher in the list unblock those below them.
160160
明确 ObjectOS 启动输入 = **Artifact**(不可变、可缓存的元数据信封)+ **Deployment Config**(业务 DB 坐标、凭据、项目身份、密钥;不进 artifact)。详见 [north-star.mdx §6.3](content/docs/concepts/north-star.mdx)
161161

162162
- [x] north-star.mdx §6.3 增补 Runtime Inputs 节(含本地单 project env 表 + 反模式说明)
163-
- [ ] 实现本地单 project env 路径:`OBJECTSTACK_PROJECT_ID` / `OBJECTSTACK_DATABASE_URL` / `OBJECTSTACK_DATABASE_DRIVER` / `OBJECTSTACK_ARTIFACT_PATH`(默认 `./dist/objectstack.json`)/ `OBJECTSTACK_CLOUD_URL`(可选,留空即离线)/ `AUTH_SECRET`
164-
- [ ] 修复 Drift:`ProjectKernelFactory` 不再直连控制面 DB 读 `sys_project` / `sys_project_credential`,改走 Artifact API + Deployment Config 注入
165-
- [ ] [apps/server/objectstack.config.ts](apps/server/objectstack.config.ts) 的 env 命名收敛到 `OBJECTSTACK_*` 前缀
163+
- [x] 实现本地单 project env 路径:`OBJECTSTACK_PROJECT_ID` / `OBJECTSTACK_DATABASE_URL` / `OBJECTSTACK_DATABASE_DRIVER` / `OBJECTSTACK_ARTIFACT_PATH`(默认 `./dist/objectstack.json`)/ `OBJECTSTACK_CLOUD_URL`(可选,留空即离线)/ `AUTH_SECRET`
164+
- [x] 修复 Drift:`ProjectKernelFactory` 不再直连控制面 DB 读 `sys_project` / `sys_project_credential`,改走 Artifact API + Deployment Config 注入`localProject` 分支)
165+
- [x] [apps/server/objectstack.config.ts](apps/server/objectstack.config.ts) 的 env 命名收敛到 `OBJECTSTACK_*` 前缀`isLocalMode` 分流本地/云端路径
166166

167167
**Resolves:** Open Question §9.2(已解决)+ 新增 Drift(`ProjectKernelFactory` 绕过 Artifact API)。
168168

apps/server/objectstack.config.ts

Lines changed: 97 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,30 @@
55
*
66
* Booted by `objectstack dev` / `objectstack serve` (see `package.json`).
77
*
8-
* The CLI loads this file, registers the plugins below on a fresh
9-
* `ObjectKernel`, then auto-mounts the standard HTTP stack
10-
* (HonoServerPlugin, Setup, RestAPI, Dispatcher, I18nService).
8+
* ## Boot modes
119
*
12-
* Our plugin list:
13-
* 1. `createControlPlanePlugins(...)` — ObjectQL + driver + tenant +
14-
* system-project + auth/security/audit + metadata for `sys_*`.
15-
* 2. `MultiProjectPlugin` — registers `env-registry`, `kernel-manager`
16-
* and `template-seeder` on the control kernel so HttpDispatcher can
17-
* route requests to per-project kernels via hostname / X-Project-Id.
10+
* ### Local mode (`OBJECTSTACK_CLOUD_URL` is unset)
1811
*
19-
* The control-plane driver is selected from a single URL-style env var
20-
* (Turso/Prisma convention):
12+
* Single-project, offline-first. No control-plane DB is required.
13+
* Required env vars:
14+
* OBJECTSTACK_PROJECT_ID — project identity (e.g. "proj_local")
15+
* OBJECTSTACK_DATABASE_URL — project business DB (file:./app.db, memory://mydb, libsql://…)
16+
* OBJECTSTACK_DATABASE_DRIVER — driver name: sqlite | memory | turso | postgres
17+
* OBJECTSTACK_ARTIFACT_PATH — path to compiled artifact (default: ./dist/objectstack.json)
18+
* AUTH_SECRET — JWT signing secret (≥32 chars)
2119
*
22-
* OBJECTSTACK_DATABASE_URL
23-
* - unset → `file:./.objectstack/data/control.db`
24-
* - `file:<path>` / path → SQLite at that path (better-sqlite3)
25-
* - `libsql://…` → libSQL/Turso
26-
* - `http(s)://…` → libSQL/sqld over HTTP
20+
* ### Cloud mode (`OBJECTSTACK_CLOUD_URL` is set)
2721
*
28-
* OBJECTSTACK_DATABASE_AUTH_TOKEN — optional, for libSQL/HTTP URLs.
22+
* Multi-project, control-plane connected.
23+
* Required env vars:
24+
* OBJECTSTACK_DATABASE_URL — control-plane DB URL
25+
* OBJECTSTACK_DATABASE_AUTH_TOKEN — optional, for libSQL/Turso URLs
26+
* AUTH_SECRET / NEXT_PUBLIC_BASE_URL — same as local
27+
*
28+
* The control-plane driver URL accepts:
29+
* - unset / `file:<path>` → SQLite (better-sqlite3) [default: .objectstack/data/control.db]
30+
* - `libsql://…` → libSQL / Turso
31+
* - `http(s)://…` → libSQL / sqld over HTTP
2932
*/
3033

3134
import { resolve as resolvePath } from 'node:path';
@@ -39,11 +42,58 @@ import { templateRegistry } from './server/templates/registry.js';
3942

4043
type IDataDriver = Contracts.IDataDriver;
4144

42-
/**
43-
* Resolve the control-plane driver from `OBJECTSTACK_DATABASE_URL`.
44-
* Drivers are loaded via dynamic import() so esbuild does not bundle
45-
* the database libraries (better-sqlite3, libsql) at parse time.
46-
*/
45+
// ── Discriminator ─────────────────────────────────────────────────────────────
46+
const isLocalMode = !process.env.OBJECTSTACK_CLOUD_URL;
47+
48+
const authSecret = process.env.AUTH_SECRET
49+
?? 'dev-secret-please-change-in-production-min-32-chars';
50+
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL
51+
?? (process.env.VERCEL_PROJECT_PRODUCTION_URL
52+
? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`
53+
: undefined)
54+
?? (process.env.VERCEL_URL
55+
? `https://${process.env.VERCEL_URL}`
56+
: undefined)
57+
?? `http://localhost:${process.env.PORT ?? 3000}`;
58+
59+
// ── LOCAL MODE ────────────────────────────────────────────────────────────────
60+
61+
const localProjectId = process.env.OBJECTSTACK_PROJECT_ID ?? 'proj_local';
62+
const localDatabaseUrl = process.env.OBJECTSTACK_DATABASE_URL
63+
?? `file:${resolvePath(process.cwd(), '.objectstack/data/local.db')}`;
64+
const localDatabaseDriver = process.env.OBJECTSTACK_DATABASE_DRIVER ?? 'sqlite';
65+
const localArtifactPath = process.env.OBJECTSTACK_ARTIFACT_PATH
66+
?? resolvePath(process.cwd(), 'dist/objectstack.json');
67+
68+
// Lazy-loading proxy for local boot: registers ObjectQL + MetadataPlugin(local-file) + Auth
69+
// on the kernel. No control-plane, no MultiProjectPlugin.
70+
const localBootPluginProxy: any = {
71+
name: 'com.objectstack.local-boot',
72+
version: '0.0.0',
73+
async init(ctx: any) {
74+
const { ObjectQLPlugin } = await import('@objectstack/objectql');
75+
const { MetadataPlugin } = await import('@objectstack/metadata');
76+
const { AuthPlugin } = await import('@objectstack/plugin-auth');
77+
78+
await ctx.kernel.use(new ObjectQLPlugin({ environmentId: localProjectId }));
79+
await ctx.kernel.use(new MetadataPlugin({
80+
watch: false,
81+
environmentId: localProjectId,
82+
artifactSource: { mode: 'local-file', path: localArtifactPath },
83+
}));
84+
await ctx.kernel.use(new AuthPlugin({ secret: authSecret, baseUrl }));
85+
86+
ctx.logger?.info?.('[LocalBoot] plugins registered', {
87+
projectId: localProjectId,
88+
databaseUrl: localDatabaseUrl,
89+
databaseDriver: localDatabaseDriver,
90+
artifactPath: localArtifactPath,
91+
});
92+
},
93+
};
94+
95+
// ── CLOUD MODE ────────────────────────────────────────────────────────────────
96+
4797
async function buildControlDriver(): Promise<{
4898
driver: IDataDriver;
4999
driverName: 'sqlite' | 'turso';
@@ -66,22 +116,7 @@ async function buildControlDriver(): Promise<{
66116
return { driver: driver as unknown as IDataDriver, driverName: 'sqlite', databaseUrl: `file:${filename}` };
67117
}
68118

69-
const authSecret = process.env.AUTH_SECRET
70-
?? 'dev-secret-please-change-in-production-min-32-chars';
71-
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL
72-
?? (process.env.VERCEL_PROJECT_PRODUCTION_URL
73-
? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`
74-
: undefined)
75-
?? (process.env.VERCEL_URL
76-
? `https://${process.env.VERCEL_URL}`
77-
: undefined)
78-
?? `http://localhost:${process.env.PORT ?? 3000}`;
79-
80119
// Per-project kernels share a minimal base. Loaded lazily on project provisioning.
81-
// System plugins (Tenant, Auth, Security, Audit) are included so that sys_*
82-
// objects are visible at project API endpoints. They declare
83-
// `defaultDatasource: 'cloud'` so their queries route through the
84-
// ControlPlaneProxyDriver (org-scoped) mounted by ProjectKernelFactory.
85120
const basePlugins: BasePluginsFactory = async ({ projectId, project }) => {
86121
const { ObjectQLPlugin } = await import('@objectstack/objectql');
87122
const { MetadataPlugin } = await import('@objectstack/metadata');
@@ -100,18 +135,14 @@ const basePlugins: BasePluginsFactory = async ({ projectId, project }) => {
100135
];
101136
};
102137

103-
// Example bundles are seeded into a project at provisioning time via
104-
// the template-seeder — not pre-installed into every project kernel.
105138
const appBundles: AppBundleResolver = {
106139
async resolve() { return []; },
107140
};
108141

109142
// Single shared promise — both control-plane plugins and MultiProjectPlugin
110-
// use the same DB connection. buildControlDriver() is called only once.
143+
// use the same DB connection.
111144
const controlDriverPromise = buildControlDriver();
112145

113-
// Lazy-loading wrapper for MultiProjectPlugin — the control driver is built
114-
// asynchronously so it is not imported at module evaluation time.
115146
const multiProjectPluginProxy: any = {
116147
name: 'com.objectstack.multi-project',
117148
version: '0.0.0',
@@ -138,19 +169,27 @@ const multiProjectPluginProxy: any = {
138169
},
139170
};
140171

141-
export default {
142-
plugins: [
143-
...createControlPlanePlugins({
144-
controlDriverPromise,
145-
authSecret,
146-
baseUrl,
147-
}),
148-
multiProjectPluginProxy,
149-
],
150-
// Project-scoping config consumed by `createRestApiPlugin` and
151-
// `createDispatcherPlugin` (auto-registered by `objectstack serve`).
152-
api: {
153-
enableProjectScoping: true,
154-
projectResolution: 'auto' as const,
155-
},
156-
};
172+
// ── Export ────────────────────────────────────────────────────────────────────
173+
174+
export default isLocalMode
175+
? {
176+
plugins: [localBootPluginProxy],
177+
api: {
178+
enableProjectScoping: false,
179+
projectResolution: 'none' as const,
180+
},
181+
}
182+
: {
183+
plugins: [
184+
...createControlPlanePlugins({
185+
controlDriverPromise,
186+
authSecret,
187+
baseUrl,
188+
}),
189+
multiProjectPluginProxy,
190+
],
191+
api: {
192+
enableProjectScoping: true,
193+
projectResolution: 'auto' as const,
194+
},
195+
};

content/docs/references/cloud/index.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This section contains all protocol schemas for the cloud layer of ObjectStack.
1313
<Card href="/docs/references/cloud/package" title="Package" description="Source: packages/spec/src/cloud/package.zod.ts" />
1414
<Card href="/docs/references/cloud/package-version" title="Package Version" description="Source: packages/spec/src/cloud/package-version.zod.ts" />
1515
<Card href="/docs/references/cloud/project" title="Project" description="Source: packages/spec/src/cloud/project.zod.ts" />
16+
<Card href="/docs/references/cloud/project-artifact" title="Project Artifact" description="Source: packages/spec/src/cloud/project-artifact.zod.ts" />
1617
<Card href="/docs/references/cloud/project-package" title="Project Package" description="Source: packages/spec/src/cloud/project-package.zod.ts" />
1718
<Card href="/docs/references/cloud/tenant" title="Tenant" description="Source: packages/spec/src/cloud/tenant.zod.ts" />
1819
</Cards>

content/docs/references/cloud/meta.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"package-version",
1010
"plugin-security",
1111
"project",
12+
"project-artifact",
1213
"project-package",
1314
"provisioning",
1415
"tenant"
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
title: Project Artifact
3+
description: Project Artifact protocol schemas
4+
---
5+
6+
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs go in content/docs/guides/. */}
7+
8+
# Project Artifact Envelope (M1)
9+
10+
Describes the response shape of `GET /api/v1/cloud/projects/:projectId/artifact`
11+
12+
— the assembled artifact ObjectOS pulls from the control plane.
13+
14+
Distinct from the marketplace `PackageArtifactSchema` (a .tgz file listing).
15+
16+
This envelope wraps the compiled `ObjectStackDefinitionSchema` produced by
17+
18+
`objectstack compile` together with control-plane assigned identity
19+
20+
(`commitId`, `checksum`).
21+
22+
<Callout type="info">
23+
**Source:** `packages/spec/src/cloud/project-artifact.zod.ts`
24+
</Callout>
25+
26+
## TypeScript Usage
27+
28+
```typescript
29+
import { Sha256Digest } from '@objectstack/spec/cloud';
30+
import type { Sha256Digest } from '@objectstack/spec/cloud';
31+
32+
// Validate data
33+
const result = Sha256Digest.parse(data);
34+
```
35+
36+
---
37+
38+
39+
---
40+

0 commit comments

Comments
 (0)