@treeseed/core 0.4.1 → 0.4.4
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/README.md +7 -1
- package/dist/api/agent-routes.d.ts +13 -0
- package/dist/api/agent-routes.js +402 -0
- package/dist/api/app.d.ts +5 -0
- package/dist/api/app.js +270 -0
- package/dist/api/auth/d1-database.d.ts +3 -0
- package/dist/api/auth/d1-database.js +24 -0
- package/dist/api/auth/d1-provider.d.ts +67 -0
- package/dist/api/auth/d1-provider.js +84 -0
- package/dist/api/auth/d1-store.d.ts +97 -0
- package/dist/api/auth/d1-store.js +631 -0
- package/dist/api/auth/memory-provider.d.ts +73 -0
- package/dist/api/auth/memory-provider.js +239 -0
- package/dist/api/auth/rbac.d.ts +22 -0
- package/dist/api/auth/rbac.js +158 -0
- package/dist/api/auth/tokens.d.ts +18 -0
- package/dist/api/auth/tokens.js +56 -0
- package/dist/api/config.d.ts +2 -0
- package/dist/api/config.js +65 -0
- package/dist/api/gateway.d.ts +5 -0
- package/dist/api/gateway.js +35 -0
- package/dist/api/http.d.ts +24 -0
- package/dist/api/http.js +44 -0
- package/dist/api/index.d.ts +9 -0
- package/dist/api/index.js +18 -0
- package/dist/api/operations-routes.d.ts +6 -0
- package/dist/api/operations-routes.js +34 -0
- package/dist/api/operations.d.ts +3 -0
- package/dist/api/operations.js +26 -0
- package/dist/api/providers.d.ts +2 -0
- package/dist/api/providers.js +61 -0
- package/dist/api/railway.d.ts +45 -0
- package/dist/api/railway.js +69 -0
- package/dist/api/sdk-dispatch.d.ts +14 -0
- package/dist/api/sdk-dispatch.js +145 -0
- package/dist/api/sdk-routes.d.ts +10 -0
- package/dist/api/sdk-routes.js +25 -0
- package/dist/api/server.d.ts +2 -0
- package/dist/api/server.js +10 -0
- package/dist/api/templates.d.ts +3 -0
- package/dist/api/templates.js +31 -0
- package/dist/api/types.d.ts +193 -0
- package/dist/api/types.js +0 -0
- package/dist/api.d.ts +1 -0
- package/dist/api.js +1 -0
- package/dist/dev.d.ts +41 -0
- package/dist/dev.js +189 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +23 -1
- package/dist/platform-resources.d.ts +37 -0
- package/dist/platform-resources.js +133 -0
- package/dist/platform.d.ts +2 -0
- package/dist/platform.js +16 -0
- package/dist/plugin-default.d.ts +1 -0
- package/dist/plugin-default.js +4 -0
- package/dist/railway.d.ts +1 -0
- package/dist/railway.js +4 -0
- package/dist/scripts/build-dist.js +7 -0
- package/dist/scripts/dev-platform.js +24 -0
- package/dist/scripts/workspace-bootstrap.js +24 -10
- package/dist/site-resources.d.ts +1 -29
- package/dist/site-resources.js +7 -120
- package/dist/site.js +3 -1
- package/package.json +37 -3
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import type { AgentSdk, SdkQueueMessageEnvelope } from '@treeseed/sdk';
|
|
2
|
+
import type { ApiPrincipal, ApiScope, DeviceCodeApproveRequest as SdkDeviceCodeApproveRequest, DeviceCodePollRequest, DeviceCodePollResponse, DeviceCodeStartRequest, DeviceCodeStartResponse, RemoteWorkflowOperationRequest as WorkflowHttpOperationRequest, RemoteWorkflowOperationResponse as ApiWorkflowOperationResponse, RemoteSdkOperationRequest as SdkHttpOperationRequest, TokenRefreshRequest, TokenRefreshResponse } from '@treeseed/sdk/remote';
|
|
3
|
+
export type { ApiPrincipal, ApiScope, DeviceCodePollRequest, DeviceCodePollResponse, DeviceCodeStartRequest, DeviceCodeStartResponse, WorkflowHttpOperationRequest, ApiWorkflowOperationResponse, SdkHttpOperationRequest, TokenRefreshRequest, TokenRefreshResponse, };
|
|
4
|
+
export type DeviceCodeApproveRequest = SdkDeviceCodeApproveRequest;
|
|
5
|
+
export interface ApiAuthProvider {
|
|
6
|
+
readonly id: string;
|
|
7
|
+
startDeviceFlow(request: DeviceCodeStartRequest): Promise<DeviceCodeStartResponse>;
|
|
8
|
+
pollDeviceFlow(request: DeviceCodePollRequest): Promise<DeviceCodePollResponse>;
|
|
9
|
+
refreshAccessToken(request: TokenRefreshRequest): Promise<TokenRefreshResponse>;
|
|
10
|
+
approveDeviceFlow(request: DeviceCodeApproveRequest): Promise<{
|
|
11
|
+
ok: true;
|
|
12
|
+
}>;
|
|
13
|
+
authenticateBearerToken(token: string): Promise<{
|
|
14
|
+
principal: ApiPrincipal;
|
|
15
|
+
credential: ApiCredential;
|
|
16
|
+
} | null>;
|
|
17
|
+
authenticateServiceCredential(serviceId: string, secret: string): Promise<{
|
|
18
|
+
principal: ApiPrincipal;
|
|
19
|
+
credential: ApiCredential;
|
|
20
|
+
} | null>;
|
|
21
|
+
createPersonalAccessToken(userId: string, input: {
|
|
22
|
+
name: string;
|
|
23
|
+
scopes?: string[];
|
|
24
|
+
expiresAt?: string | null;
|
|
25
|
+
}): Promise<{
|
|
26
|
+
id: string;
|
|
27
|
+
token: string;
|
|
28
|
+
prefix: string;
|
|
29
|
+
name: string;
|
|
30
|
+
expiresAt: string | null;
|
|
31
|
+
}>;
|
|
32
|
+
listPersonalAccessTokens(userId: string): Promise<Array<{
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
token_prefix: string;
|
|
36
|
+
expires_at: string | null;
|
|
37
|
+
last_used_at: string | null;
|
|
38
|
+
revoked_at: string | null;
|
|
39
|
+
created_at: string;
|
|
40
|
+
}>>;
|
|
41
|
+
revokePersonalAccessToken(userId: string, tokenId: string): Promise<void>;
|
|
42
|
+
syncUserIdentity(identity: UserIdentityProfileInput): Promise<{
|
|
43
|
+
principal: ApiPrincipal;
|
|
44
|
+
userId: string;
|
|
45
|
+
identityId: string | null;
|
|
46
|
+
}>;
|
|
47
|
+
createServiceToken(input: {
|
|
48
|
+
serviceId: string;
|
|
49
|
+
name: string;
|
|
50
|
+
roles?: string[];
|
|
51
|
+
permissions?: string[];
|
|
52
|
+
}): Promise<{
|
|
53
|
+
id: string;
|
|
54
|
+
serviceId: string;
|
|
55
|
+
secret: string;
|
|
56
|
+
}>;
|
|
57
|
+
rotateServiceToken(serviceId: string): Promise<{
|
|
58
|
+
id: string;
|
|
59
|
+
serviceId: string;
|
|
60
|
+
secret: string;
|
|
61
|
+
}>;
|
|
62
|
+
createTrustedUserAssertion(claims: TrustedUserAssertionClaims): string;
|
|
63
|
+
verifyTrustedUserAssertion(assertion: string): TrustedUserAssertionClaims | null;
|
|
64
|
+
exchangeTrustedUserAssertion(claims: TrustedUserAssertionClaims): Promise<{
|
|
65
|
+
ok: true;
|
|
66
|
+
accessToken: string;
|
|
67
|
+
tokenType: 'Bearer';
|
|
68
|
+
expiresAt: string;
|
|
69
|
+
expiresInSeconds: number;
|
|
70
|
+
principal: ApiPrincipal;
|
|
71
|
+
}>;
|
|
72
|
+
}
|
|
73
|
+
export type ApiRuntimeProviderSelections = {
|
|
74
|
+
auth: string;
|
|
75
|
+
agents: {
|
|
76
|
+
execution: string;
|
|
77
|
+
queue: string;
|
|
78
|
+
notification: string;
|
|
79
|
+
repository: string;
|
|
80
|
+
verification: string;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
export interface ApiConfig {
|
|
84
|
+
name: string;
|
|
85
|
+
host: string;
|
|
86
|
+
port: number;
|
|
87
|
+
baseUrl: string;
|
|
88
|
+
issuer: string;
|
|
89
|
+
repoRoot: string;
|
|
90
|
+
authSecret: string;
|
|
91
|
+
cloudflareAccountId?: string;
|
|
92
|
+
cloudflareApiToken?: string;
|
|
93
|
+
d1DatabaseId?: string;
|
|
94
|
+
d1DatabaseName?: string;
|
|
95
|
+
d1LocalPersistTo?: string;
|
|
96
|
+
webServiceId: string;
|
|
97
|
+
webServiceSecret: string;
|
|
98
|
+
webAssertionSecret: string;
|
|
99
|
+
webExchangeTtlSeconds: number;
|
|
100
|
+
bootstrapAdminAllowlist: string[];
|
|
101
|
+
accessTokenTtlSeconds: number;
|
|
102
|
+
refreshTokenTtlSeconds: number;
|
|
103
|
+
deviceCodeTtlSeconds: number;
|
|
104
|
+
deviceCodePollIntervalSeconds: number;
|
|
105
|
+
templateCatalogPath?: string;
|
|
106
|
+
providers: ApiRuntimeProviderSelections;
|
|
107
|
+
}
|
|
108
|
+
export interface AppVariables {
|
|
109
|
+
requestId: string;
|
|
110
|
+
config: ApiConfig;
|
|
111
|
+
principal: ApiPrincipal | null;
|
|
112
|
+
actingUser: ApiPrincipal | null;
|
|
113
|
+
credential: ApiCredential | null;
|
|
114
|
+
actorType: 'anonymous' | 'user' | 'service';
|
|
115
|
+
permissionGrants: string[];
|
|
116
|
+
}
|
|
117
|
+
export interface ApiCredential {
|
|
118
|
+
type: 'access_token' | 'personal_access_token' | 'service_secret' | 'service_token';
|
|
119
|
+
id: string;
|
|
120
|
+
label?: string;
|
|
121
|
+
}
|
|
122
|
+
export interface TrustedUserAssertionClaims {
|
|
123
|
+
userId: string;
|
|
124
|
+
sessionId: string;
|
|
125
|
+
identityId?: string | null;
|
|
126
|
+
authTime: string;
|
|
127
|
+
expiresAt: string;
|
|
128
|
+
nonce: string;
|
|
129
|
+
}
|
|
130
|
+
export interface UserIdentityProfileInput {
|
|
131
|
+
provider: string;
|
|
132
|
+
providerSubject: string;
|
|
133
|
+
email?: string | null;
|
|
134
|
+
emailVerified?: boolean;
|
|
135
|
+
displayName?: string | null;
|
|
136
|
+
profile?: Record<string, unknown>;
|
|
137
|
+
}
|
|
138
|
+
export type ApiProviderFactory<T> = (options: {
|
|
139
|
+
config: ApiConfig;
|
|
140
|
+
}) => T;
|
|
141
|
+
export interface ApiRuntimeProviders {
|
|
142
|
+
auth?: Record<string, ApiProviderFactory<ApiAuthProvider>>;
|
|
143
|
+
agentExecution?: Record<string, unknown>;
|
|
144
|
+
agentQueue?: Record<string, unknown>;
|
|
145
|
+
agentNotification?: Record<string, unknown>;
|
|
146
|
+
agentRepository?: Record<string, unknown>;
|
|
147
|
+
agentVerification?: Record<string, unknown>;
|
|
148
|
+
}
|
|
149
|
+
export interface ResolvedApiRuntimeProviders {
|
|
150
|
+
auth: ApiAuthProvider;
|
|
151
|
+
registries: {
|
|
152
|
+
auth: Map<string, ApiProviderFactory<ApiAuthProvider>>;
|
|
153
|
+
agentExecution: Map<string, unknown>;
|
|
154
|
+
agentQueue: Map<string, unknown>;
|
|
155
|
+
agentNotification: Map<string, unknown>;
|
|
156
|
+
agentRepository: Map<string, unknown>;
|
|
157
|
+
agentVerification: Map<string, unknown>;
|
|
158
|
+
};
|
|
159
|
+
selections: ApiRuntimeProviderSelections;
|
|
160
|
+
}
|
|
161
|
+
export interface ApiServerOptions {
|
|
162
|
+
config?: Partial<ApiConfig>;
|
|
163
|
+
runtimeProviders?: ApiRuntimeProviders;
|
|
164
|
+
sdk?: AgentSdk;
|
|
165
|
+
workflowExecutor?: (operation: string, request: WorkflowHttpOperationRequest) => Promise<ApiWorkflowOperationResponse>;
|
|
166
|
+
surfaces?: Partial<{
|
|
167
|
+
auth: boolean;
|
|
168
|
+
templates: boolean;
|
|
169
|
+
sdk: boolean;
|
|
170
|
+
agent: boolean;
|
|
171
|
+
operations: boolean;
|
|
172
|
+
}>;
|
|
173
|
+
scopes?: Partial<{
|
|
174
|
+
authMe: ApiScope;
|
|
175
|
+
sdk: ApiScope;
|
|
176
|
+
agent: ApiScope;
|
|
177
|
+
operations: ApiScope;
|
|
178
|
+
}>;
|
|
179
|
+
log?: (message: string, details?: Record<string, unknown>) => void;
|
|
180
|
+
}
|
|
181
|
+
export interface GatewayQueueProducer {
|
|
182
|
+
enqueue(request: {
|
|
183
|
+
queueName?: string;
|
|
184
|
+
message: SdkQueueMessageEnvelope;
|
|
185
|
+
delaySeconds?: number;
|
|
186
|
+
}): Promise<void>;
|
|
187
|
+
}
|
|
188
|
+
export interface GatewayServerOptions {
|
|
189
|
+
sdk: AgentSdk;
|
|
190
|
+
bearerToken: string;
|
|
191
|
+
queueProducer?: GatewayQueueProducer;
|
|
192
|
+
projectId?: string;
|
|
193
|
+
}
|
|
File without changes
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './api/index';
|
package/dist/api.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./api/index.js";
|
package/dist/dev.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ChildProcess, SpawnOptions } from 'node:child_process';
|
|
2
|
+
export declare const TREESEED_DEFAULT_WEB_HOST = "127.0.0.1";
|
|
3
|
+
export declare const TREESEED_DEFAULT_WEB_PORT = 4321;
|
|
4
|
+
export declare const TREESEED_DEFAULT_API_HOST = "127.0.0.1";
|
|
5
|
+
export declare const TREESEED_DEFAULT_API_PORT = 3000;
|
|
6
|
+
export type TreeseedIntegratedDevSurface = 'integrated' | 'web' | 'api';
|
|
7
|
+
export type TreeseedIntegratedDevOptions = {
|
|
8
|
+
surface?: TreeseedIntegratedDevSurface;
|
|
9
|
+
watch?: boolean;
|
|
10
|
+
cwd?: string;
|
|
11
|
+
stdio?: SpawnOptions['stdio'];
|
|
12
|
+
env?: NodeJS.ProcessEnv;
|
|
13
|
+
webHost?: string;
|
|
14
|
+
webPort?: number;
|
|
15
|
+
apiHost?: string;
|
|
16
|
+
apiPort?: number;
|
|
17
|
+
};
|
|
18
|
+
export type TreeseedIntegratedDevCommand = {
|
|
19
|
+
id: 'web' | 'api';
|
|
20
|
+
label: string;
|
|
21
|
+
command: string;
|
|
22
|
+
args: string[];
|
|
23
|
+
cwd: string;
|
|
24
|
+
env: NodeJS.ProcessEnv;
|
|
25
|
+
};
|
|
26
|
+
export type TreeseedIntegratedDevPlan = {
|
|
27
|
+
surface: TreeseedIntegratedDevSurface;
|
|
28
|
+
tenantRoot: string;
|
|
29
|
+
apiBaseUrl: string;
|
|
30
|
+
commands: TreeseedIntegratedDevCommand[];
|
|
31
|
+
};
|
|
32
|
+
type SpawnLike = (command: string, args: string[], options: SpawnOptions) => ChildProcess;
|
|
33
|
+
type SignalRegistrar = (signal: NodeJS.Signals, handler: () => void) => () => void;
|
|
34
|
+
type TreeseedIntegratedDevDependencies = {
|
|
35
|
+
spawn: SpawnLike;
|
|
36
|
+
onSignal: SignalRegistrar;
|
|
37
|
+
prepareEnvironment: (tenantRoot: string) => void;
|
|
38
|
+
};
|
|
39
|
+
export declare function createTreeseedIntegratedDevPlan(options?: TreeseedIntegratedDevOptions): TreeseedIntegratedDevPlan;
|
|
40
|
+
export declare function runTreeseedIntegratedDev(options?: TreeseedIntegratedDevOptions, deps?: Partial<TreeseedIntegratedDevDependencies>): Promise<number>;
|
|
41
|
+
export {};
|
package/dist/dev.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { applyTreeseedEnvironmentToProcess, assertTreeseedCommandEnvironment } from "@treeseed/sdk/workflow-support";
|
|
7
|
+
const require2 = createRequire(import.meta.url);
|
|
8
|
+
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
9
|
+
const TREESEED_DEFAULT_WEB_HOST = "127.0.0.1";
|
|
10
|
+
const TREESEED_DEFAULT_WEB_PORT = 4321;
|
|
11
|
+
const TREESEED_DEFAULT_API_HOST = "127.0.0.1";
|
|
12
|
+
const TREESEED_DEFAULT_API_PORT = 3e3;
|
|
13
|
+
function resolvePackageRoot(packageName, tenantRoot) {
|
|
14
|
+
const resolvedPath = require2.resolve(packageName, {
|
|
15
|
+
paths: [tenantRoot, packageRoot, process.cwd()]
|
|
16
|
+
});
|
|
17
|
+
let currentDir = dirname(resolvedPath);
|
|
18
|
+
while (!existsSync(resolve(currentDir, "package.json"))) {
|
|
19
|
+
const parentDir = dirname(currentDir);
|
|
20
|
+
if (parentDir === currentDir) {
|
|
21
|
+
throw new Error(`Unable to resolve package root for "${packageName}".`);
|
|
22
|
+
}
|
|
23
|
+
currentDir = parentDir;
|
|
24
|
+
}
|
|
25
|
+
return currentDir;
|
|
26
|
+
}
|
|
27
|
+
function resolveNodeEntrypoint(packageDir, sourceRelativePath, distRelativePath) {
|
|
28
|
+
const sourcePath = resolve(packageDir, sourceRelativePath);
|
|
29
|
+
const runTsPath = resolve(packageDir, "scripts", "run-ts.mjs");
|
|
30
|
+
if (existsSync(sourcePath) && existsSync(runTsPath)) {
|
|
31
|
+
return {
|
|
32
|
+
command: process.execPath,
|
|
33
|
+
args: [runTsPath, sourcePath]
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
command: process.execPath,
|
|
38
|
+
args: [resolve(packageDir, distRelativePath)]
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function withWatchArgs(args, watchPaths) {
|
|
42
|
+
return watchPaths.flatMap((watchPath) => ["--watch-path", watchPath]).concat(args);
|
|
43
|
+
}
|
|
44
|
+
function normalizePort(value, fallback) {
|
|
45
|
+
return Number.isInteger(value) && Number(value) > 0 ? Number(value) : fallback;
|
|
46
|
+
}
|
|
47
|
+
function createTreeseedIntegratedDevPlan(options = {}) {
|
|
48
|
+
const tenantRoot = resolve(options.cwd ?? process.cwd());
|
|
49
|
+
const surface = options.surface ?? "integrated";
|
|
50
|
+
const watch = options.watch === true;
|
|
51
|
+
const webHost = options.webHost ?? TREESEED_DEFAULT_WEB_HOST;
|
|
52
|
+
const webPort = normalizePort(options.webPort, TREESEED_DEFAULT_WEB_PORT);
|
|
53
|
+
const apiHost = options.apiHost ?? TREESEED_DEFAULT_API_HOST;
|
|
54
|
+
const apiPort = normalizePort(options.apiPort, TREESEED_DEFAULT_API_PORT);
|
|
55
|
+
const mergedEnv = { ...process.env, ...options.env ?? {} };
|
|
56
|
+
const apiBaseUrl = mergedEnv.TREESEED_API_BASE_URL?.trim() || `http://${apiHost}:${apiPort}`;
|
|
57
|
+
const sdkPackageRoot = resolvePackageRoot("@treeseed/sdk", tenantRoot);
|
|
58
|
+
const webEntrypoint = resolveNodeEntrypoint(
|
|
59
|
+
sdkPackageRoot,
|
|
60
|
+
"scripts/tenant-astro-command.ts",
|
|
61
|
+
"dist/scripts/tenant-astro-command.js"
|
|
62
|
+
);
|
|
63
|
+
const apiEntrypoint = resolveNodeEntrypoint(
|
|
64
|
+
packageRoot,
|
|
65
|
+
"src/api/server.ts",
|
|
66
|
+
"dist/api/server.js"
|
|
67
|
+
);
|
|
68
|
+
const watchPaths = [
|
|
69
|
+
resolve(packageRoot, existsSync(resolve(packageRoot, "src")) ? "src" : "dist"),
|
|
70
|
+
resolve(tenantRoot, "src"),
|
|
71
|
+
resolve(tenantRoot, "treeseed.site.yaml"),
|
|
72
|
+
resolve(tenantRoot, "astro.config.ts")
|
|
73
|
+
];
|
|
74
|
+
const sharedEnv = {
|
|
75
|
+
...mergedEnv,
|
|
76
|
+
TREESEED_LOCAL_DEV_MODE: mergedEnv.TREESEED_LOCAL_DEV_MODE ?? "cloudflare",
|
|
77
|
+
TREESEED_API_BASE_URL: apiBaseUrl
|
|
78
|
+
};
|
|
79
|
+
if (watch) {
|
|
80
|
+
sharedEnv.TREESEED_PUBLIC_DEV_WATCH_RELOAD = sharedEnv.TREESEED_PUBLIC_DEV_WATCH_RELOAD || "true";
|
|
81
|
+
}
|
|
82
|
+
const commands = [];
|
|
83
|
+
if (surface === "integrated" || surface === "web") {
|
|
84
|
+
commands.push({
|
|
85
|
+
id: "web",
|
|
86
|
+
label: "Astro UI",
|
|
87
|
+
command: webEntrypoint.command,
|
|
88
|
+
args: [...webEntrypoint.args, "dev", "--host", webHost, "--port", String(webPort)],
|
|
89
|
+
cwd: tenantRoot,
|
|
90
|
+
env: sharedEnv
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (surface === "integrated" || surface === "api") {
|
|
94
|
+
commands.push({
|
|
95
|
+
id: "api",
|
|
96
|
+
label: "Hono API",
|
|
97
|
+
command: apiEntrypoint.command,
|
|
98
|
+
args: watch ? withWatchArgs(apiEntrypoint.args, watchPaths) : apiEntrypoint.args,
|
|
99
|
+
cwd: tenantRoot,
|
|
100
|
+
env: {
|
|
101
|
+
...sharedEnv,
|
|
102
|
+
PORT: sharedEnv.PORT ?? String(apiPort)
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
surface,
|
|
108
|
+
tenantRoot,
|
|
109
|
+
apiBaseUrl,
|
|
110
|
+
commands
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function defaultSignalRegistrar(signal, handler) {
|
|
114
|
+
process.on(signal, handler);
|
|
115
|
+
return () => {
|
|
116
|
+
process.off(signal, handler);
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function defaultPrepareEnvironment(tenantRoot) {
|
|
120
|
+
applyTreeseedEnvironmentToProcess({ tenantRoot, scope: "local", override: true });
|
|
121
|
+
assertTreeseedCommandEnvironment({ tenantRoot, scope: "local", purpose: "dev" });
|
|
122
|
+
}
|
|
123
|
+
function stopChildProcess(child, signal = "SIGTERM") {
|
|
124
|
+
if (!child || typeof child.kill !== "function") {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
child.kill(signal);
|
|
129
|
+
} catch {
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
133
|
+
const tenantRoot = resolve(options.cwd ?? process.cwd());
|
|
134
|
+
const prepareEnvironment = deps.prepareEnvironment ?? defaultPrepareEnvironment;
|
|
135
|
+
prepareEnvironment(tenantRoot);
|
|
136
|
+
const plan = createTreeseedIntegratedDevPlan({
|
|
137
|
+
...options,
|
|
138
|
+
cwd: tenantRoot,
|
|
139
|
+
env: {
|
|
140
|
+
...process.env,
|
|
141
|
+
...options.env ?? {}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
const spawnProcess = deps.spawn ?? spawn;
|
|
145
|
+
const onSignal = deps.onSignal ?? defaultSignalRegistrar;
|
|
146
|
+
const children = /* @__PURE__ */ new Map();
|
|
147
|
+
let settled = false;
|
|
148
|
+
return await new Promise((resolveExitCode) => {
|
|
149
|
+
const disposers = [
|
|
150
|
+
onSignal("SIGINT", () => finalize(130)),
|
|
151
|
+
onSignal("SIGTERM", () => finalize(143))
|
|
152
|
+
];
|
|
153
|
+
function finalize(exitCode, originId) {
|
|
154
|
+
if (settled) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
settled = true;
|
|
158
|
+
for (const [childId, child] of children.entries()) {
|
|
159
|
+
if (childId !== originId) {
|
|
160
|
+
stopChildProcess(child);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
for (const dispose of disposers) {
|
|
164
|
+
dispose();
|
|
165
|
+
}
|
|
166
|
+
resolveExitCode(exitCode);
|
|
167
|
+
}
|
|
168
|
+
for (const command of plan.commands) {
|
|
169
|
+
const child = spawnProcess(command.command, command.args, {
|
|
170
|
+
cwd: command.cwd,
|
|
171
|
+
env: command.env,
|
|
172
|
+
stdio: options.stdio ?? "inherit"
|
|
173
|
+
});
|
|
174
|
+
children.set(command.id, child);
|
|
175
|
+
child.on("exit", (code, signal) => {
|
|
176
|
+
const exitCode = signal === "SIGINT" ? 130 : signal === "SIGTERM" ? 143 : code ?? 0;
|
|
177
|
+
finalize(exitCode, command.id);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
export {
|
|
183
|
+
TREESEED_DEFAULT_API_HOST,
|
|
184
|
+
TREESEED_DEFAULT_API_PORT,
|
|
185
|
+
TREESEED_DEFAULT_WEB_HOST,
|
|
186
|
+
TREESEED_DEFAULT_WEB_PORT,
|
|
187
|
+
createTreeseedIntegratedDevPlan,
|
|
188
|
+
runTreeseedIntegratedDev
|
|
189
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { buildTreeseedSiteLayers, resolveTreeseedPageEntrypoint, resolveTreeseedSiteResource, resolveTreeseedStyleEntrypoint, TREESEED_SITE_RESOURCE_KINDS, } from './site-resources';
|
|
2
|
+
export { buildTreeseedPlatformLayers, resolveTreeseedPlatformResource, TREESEED_PLATFORM_RESOURCE_KINDS, } from './platform-resources';
|
|
3
|
+
export { parseSiteConfig } from './utils/site-config-schema.js';
|
|
4
|
+
export { createTreeseedApiApp } from './api/app';
|
|
5
|
+
export { createTreeseedGatewayApp } from './api/gateway';
|
|
6
|
+
export { createRailwayTreeseedApiServer } from './api/railway';
|
|
7
|
+
export { resolveApiConfig } from './api/config';
|
|
8
|
+
export { createTreeseedIntegratedDevPlan, runTreeseedIntegratedDev, type TreeseedIntegratedDevCommand, type TreeseedIntegratedDevOptions, type TreeseedIntegratedDevPlan, type TreeseedIntegratedDevSurface, } from './dev';
|
|
9
|
+
export type * from './api/types';
|
package/dist/index.js
CHANGED
|
@@ -5,12 +5,34 @@ import {
|
|
|
5
5
|
resolveTreeseedStyleEntrypoint,
|
|
6
6
|
TREESEED_SITE_RESOURCE_KINDS
|
|
7
7
|
} from "./site-resources.js";
|
|
8
|
+
import {
|
|
9
|
+
buildTreeseedPlatformLayers,
|
|
10
|
+
resolveTreeseedPlatformResource,
|
|
11
|
+
TREESEED_PLATFORM_RESOURCE_KINDS
|
|
12
|
+
} from "./platform-resources.js";
|
|
8
13
|
import { parseSiteConfig } from "./utils/site-config-schema.js";
|
|
14
|
+
import { createTreeseedApiApp } from "./api/app.js";
|
|
15
|
+
import { createTreeseedGatewayApp } from "./api/gateway.js";
|
|
16
|
+
import { createRailwayTreeseedApiServer } from "./api/railway.js";
|
|
17
|
+
import { resolveApiConfig } from "./api/config.js";
|
|
18
|
+
import {
|
|
19
|
+
createTreeseedIntegratedDevPlan,
|
|
20
|
+
runTreeseedIntegratedDev
|
|
21
|
+
} from "./dev.js";
|
|
9
22
|
export {
|
|
23
|
+
TREESEED_PLATFORM_RESOURCE_KINDS,
|
|
10
24
|
TREESEED_SITE_RESOURCE_KINDS,
|
|
25
|
+
buildTreeseedPlatformLayers,
|
|
11
26
|
buildTreeseedSiteLayers,
|
|
27
|
+
createRailwayTreeseedApiServer,
|
|
28
|
+
createTreeseedApiApp,
|
|
29
|
+
createTreeseedGatewayApp,
|
|
30
|
+
createTreeseedIntegratedDevPlan,
|
|
12
31
|
parseSiteConfig,
|
|
32
|
+
resolveApiConfig,
|
|
13
33
|
resolveTreeseedPageEntrypoint,
|
|
34
|
+
resolveTreeseedPlatformResource,
|
|
14
35
|
resolveTreeseedSiteResource,
|
|
15
|
-
resolveTreeseedStyleEntrypoint
|
|
36
|
+
resolveTreeseedStyleEntrypoint,
|
|
37
|
+
runTreeseedIntegratedDev
|
|
16
38
|
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { TreeseedDeployConfig, TreeseedPlatformLayerDefinition, TreeseedPlatformResourceKind, TreeseedPlatformSurfaceName, TreeseedTenantConfig } from '@treeseed/sdk/platform/contracts';
|
|
2
|
+
import type { LoadedTreeseedPluginEntry } from '@treeseed/sdk/platform/plugins';
|
|
3
|
+
export declare const TREESEED_PLATFORM_RESOURCE_KINDS: readonly ["pages", "styles", "components", "routes", "middleware", "handlers", "config"];
|
|
4
|
+
export declare const TREESEED_SITE_RESOURCE_KINDS: readonly ["pages", "styles", "components"];
|
|
5
|
+
export type TreeseedSiteResourceKind = (typeof TREESEED_SITE_RESOURCE_KINDS)[number];
|
|
6
|
+
export type TreeseedPlatformLayer = {
|
|
7
|
+
owner: string;
|
|
8
|
+
surface: TreeseedPlatformSurfaceName;
|
|
9
|
+
root: string;
|
|
10
|
+
kinds: TreeseedPlatformResourceKind[];
|
|
11
|
+
};
|
|
12
|
+
export type TreeseedSiteLayerDefinition = TreeseedPlatformLayerDefinition & {
|
|
13
|
+
kinds?: TreeseedSiteResourceKind[];
|
|
14
|
+
};
|
|
15
|
+
export type TreeseedSiteLayer = TreeseedPlatformLayer & {
|
|
16
|
+
kinds: TreeseedSiteResourceKind[];
|
|
17
|
+
surface: 'web';
|
|
18
|
+
};
|
|
19
|
+
type PlatformLayerBuildContext = {
|
|
20
|
+
projectRoot: string;
|
|
21
|
+
tenantConfig: TreeseedTenantConfig;
|
|
22
|
+
siteConfig?: unknown;
|
|
23
|
+
deployConfig?: TreeseedDeployConfig;
|
|
24
|
+
coreRoot: string;
|
|
25
|
+
surface: TreeseedPlatformSurfaceName;
|
|
26
|
+
defaultKinds: TreeseedPlatformResourceKind[];
|
|
27
|
+
};
|
|
28
|
+
type PlatformLayerRuntime = {
|
|
29
|
+
plugins: LoadedTreeseedPluginEntry[];
|
|
30
|
+
};
|
|
31
|
+
export declare function buildTreeseedPlatformLayers(pluginRuntime: PlatformLayerRuntime, context: PlatformLayerBuildContext): TreeseedPlatformLayer[];
|
|
32
|
+
export declare function resolveTreeseedPlatformResource(layers: TreeseedPlatformLayer[], kind: TreeseedPlatformResourceKind, resourcePath: string): string;
|
|
33
|
+
export declare function buildTreeseedSiteLayers(pluginRuntime: PlatformLayerRuntime, context: Omit<PlatformLayerBuildContext, 'surface' | 'defaultKinds'>): TreeseedSiteLayer[];
|
|
34
|
+
export declare function resolveTreeseedSiteResource(layers: TreeseedSiteLayer[], kind: TreeseedSiteResourceKind, resourcePath: string): string;
|
|
35
|
+
export declare function resolveTreeseedPageEntrypoint(layers: TreeseedSiteLayer[], resourcePath: string): string;
|
|
36
|
+
export declare function resolveTreeseedStyleEntrypoint(layers: TreeseedSiteLayer[], resourcePath: string): string;
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
const TREESEED_PLATFORM_RESOURCE_KINDS = [
|
|
4
|
+
"pages",
|
|
5
|
+
"styles",
|
|
6
|
+
"components",
|
|
7
|
+
"routes",
|
|
8
|
+
"middleware",
|
|
9
|
+
"handlers",
|
|
10
|
+
"config"
|
|
11
|
+
];
|
|
12
|
+
const TREESEED_SITE_RESOURCE_KINDS = ["pages", "styles", "components"];
|
|
13
|
+
const PLATFORM_RESOURCE_KIND_SET = new Set(TREESEED_PLATFORM_RESOURCE_KINDS);
|
|
14
|
+
function normalizeKinds(kinds, fallbackKinds) {
|
|
15
|
+
const normalized = kinds?.length ? [...new Set(kinds)] : [...fallbackKinds];
|
|
16
|
+
for (const kind of normalized) {
|
|
17
|
+
if (!PLATFORM_RESOURCE_KIND_SET.has(kind)) {
|
|
18
|
+
throw new Error(`Unknown Treeseed platform resource kind "${kind}".`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return normalized;
|
|
22
|
+
}
|
|
23
|
+
function normalizeResourcePath(kind, resourcePath) {
|
|
24
|
+
const normalized = resourcePath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
25
|
+
return normalized.startsWith(`${kind}/`) ? normalized : `${kind}/${normalized}`;
|
|
26
|
+
}
|
|
27
|
+
function isKindSupported(layer, kind) {
|
|
28
|
+
return layer.kinds.includes(kind);
|
|
29
|
+
}
|
|
30
|
+
function getTenantRoot(tenantConfig, projectRoot) {
|
|
31
|
+
return tenantConfig.__tenantRoot ?? projectRoot;
|
|
32
|
+
}
|
|
33
|
+
function normalizeLayerDefinition(owner, baseRoot, layer, context) {
|
|
34
|
+
return {
|
|
35
|
+
owner,
|
|
36
|
+
surface: context.surface,
|
|
37
|
+
root: resolve(baseRoot, layer.root),
|
|
38
|
+
kinds: normalizeKinds(layer.kinds, context.defaultKinds)
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function getPluginLayers(entry, context) {
|
|
42
|
+
const plugin = entry.plugin;
|
|
43
|
+
const platformLayers = typeof plugin.platformLayers === "function" ? plugin.platformLayers({ ...context, pluginConfig: entry.config ?? {} }) : plugin.platformLayers;
|
|
44
|
+
const surfaceLayers = (platformLayers ?? []).filter((layer) => !layer.surface || layer.surface === context.surface).map((layer) => normalizeLayerDefinition(entry.package, entry.baseDir, layer, context));
|
|
45
|
+
if (context.surface !== "web") {
|
|
46
|
+
return surfaceLayers;
|
|
47
|
+
}
|
|
48
|
+
const legacySiteLayers = typeof plugin.siteLayers === "function" ? plugin.siteLayers({ ...context, pluginConfig: entry.config ?? {} }) : plugin.siteLayers;
|
|
49
|
+
return [
|
|
50
|
+
...surfaceLayers,
|
|
51
|
+
...(legacySiteLayers ?? []).map((layer) => normalizeLayerDefinition(entry.package, entry.baseDir, layer, context))
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
function getTenantLayers(context) {
|
|
55
|
+
const tenantRoot = getTenantRoot(context.tenantConfig, context.projectRoot);
|
|
56
|
+
const surfaceOverrides = context.tenantConfig.overrides?.surfaces?.[context.surface];
|
|
57
|
+
const legacyWebLayers = context.surface === "web" ? [
|
|
58
|
+
context.tenantConfig.overrides?.pagesRoot ? { root: context.tenantConfig.overrides.pagesRoot, kinds: ["pages"] } : null,
|
|
59
|
+
context.tenantConfig.overrides?.stylesRoot ? { root: context.tenantConfig.overrides.stylesRoot, kinds: ["styles"] } : null,
|
|
60
|
+
context.tenantConfig.overrides?.componentsRoot ? { root: context.tenantConfig.overrides.componentsRoot, kinds: ["components"] } : null
|
|
61
|
+
].filter(Boolean) : [];
|
|
62
|
+
return [...surfaceOverrides?.layers ?? [], ...legacyWebLayers].map(
|
|
63
|
+
(layer) => normalizeLayerDefinition("tenant", tenantRoot, layer, context)
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
function buildTreeseedPlatformLayers(pluginRuntime, context) {
|
|
67
|
+
const layers = [
|
|
68
|
+
{
|
|
69
|
+
owner: "@treeseed/core",
|
|
70
|
+
surface: context.surface,
|
|
71
|
+
root: context.coreRoot,
|
|
72
|
+
kinds: [...context.defaultKinds]
|
|
73
|
+
}
|
|
74
|
+
];
|
|
75
|
+
for (const entry of pluginRuntime.plugins) {
|
|
76
|
+
layers.push(...getPluginLayers(entry, context));
|
|
77
|
+
}
|
|
78
|
+
layers.push(...getTenantLayers(context));
|
|
79
|
+
return layers;
|
|
80
|
+
}
|
|
81
|
+
function resolveTreeseedPlatformResource(layers, kind, resourcePath) {
|
|
82
|
+
const normalizedPath = normalizeResourcePath(kind, resourcePath);
|
|
83
|
+
for (let index = layers.length - 1; index >= 0; index -= 1) {
|
|
84
|
+
const layer = layers[index];
|
|
85
|
+
if (!layer || !isKindSupported(layer, kind)) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const candidate = resolve(layer.root, normalizedPath);
|
|
89
|
+
if (existsSync(candidate)) {
|
|
90
|
+
return candidate;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
function buildTreeseedSiteLayers(pluginRuntime, context) {
|
|
96
|
+
return buildTreeseedPlatformLayers(pluginRuntime, {
|
|
97
|
+
...context,
|
|
98
|
+
surface: "web",
|
|
99
|
+
defaultKinds: [...TREESEED_SITE_RESOURCE_KINDS]
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
function resolveTreeseedSiteResource(layers, kind, resourcePath) {
|
|
103
|
+
return resolveTreeseedPlatformResource(layers, kind, resourcePath);
|
|
104
|
+
}
|
|
105
|
+
function resolveTreeseedPageEntrypoint(layers, resourcePath) {
|
|
106
|
+
const hasExplicitExtension = /\.[A-Za-z0-9]+$/u.test(resourcePath);
|
|
107
|
+
const candidates = hasExplicitExtension ? [resourcePath, `${resourcePath}.astro`, `${resourcePath}.ts`, `${resourcePath}.js`, `${resourcePath}.mjs`] : [resourcePath, `${resourcePath}.astro`, `${resourcePath}.ts`, `${resourcePath}.js`, `${resourcePath}.mjs`];
|
|
108
|
+
for (const candidate of candidates) {
|
|
109
|
+
const resolved = resolveTreeseedSiteResource(layers, "pages", candidate);
|
|
110
|
+
if (resolved) {
|
|
111
|
+
return resolved;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const normalized = hasExplicitExtension ? normalizeResourcePath("pages", resourcePath) : normalizeResourcePath("pages", `${resourcePath}.astro`);
|
|
115
|
+
throw new Error(`Unable to resolve Treeseed page resource "${normalized}".`);
|
|
116
|
+
}
|
|
117
|
+
function resolveTreeseedStyleEntrypoint(layers, resourcePath) {
|
|
118
|
+
const resolved = resolveTreeseedSiteResource(layers, "styles", resourcePath);
|
|
119
|
+
if (!resolved) {
|
|
120
|
+
throw new Error(`Unable to resolve Treeseed style resource "${normalizeResourcePath("styles", resourcePath)}".`);
|
|
121
|
+
}
|
|
122
|
+
return resolved;
|
|
123
|
+
}
|
|
124
|
+
export {
|
|
125
|
+
TREESEED_PLATFORM_RESOURCE_KINDS,
|
|
126
|
+
TREESEED_SITE_RESOURCE_KINDS,
|
|
127
|
+
buildTreeseedPlatformLayers,
|
|
128
|
+
buildTreeseedSiteLayers,
|
|
129
|
+
resolveTreeseedPageEntrypoint,
|
|
130
|
+
resolveTreeseedPlatformResource,
|
|
131
|
+
resolveTreeseedSiteResource,
|
|
132
|
+
resolveTreeseedStyleEntrypoint
|
|
133
|
+
};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { buildTreeseedPlatformLayers, resolveTreeseedPlatformResource, TREESEED_PLATFORM_RESOURCE_KINDS, type TreeseedPlatformLayer, } from './platform-resources';
|
|
2
|
+
export { createTreeseedIntegratedDevPlan, runTreeseedIntegratedDev, type TreeseedIntegratedDevCommand, type TreeseedIntegratedDevOptions, type TreeseedIntegratedDevPlan, type TreeseedIntegratedDevSurface, } from './dev';
|
package/dist/platform.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildTreeseedPlatformLayers,
|
|
3
|
+
resolveTreeseedPlatformResource,
|
|
4
|
+
TREESEED_PLATFORM_RESOURCE_KINDS
|
|
5
|
+
} from "./platform-resources.js";
|
|
6
|
+
import {
|
|
7
|
+
createTreeseedIntegratedDevPlan,
|
|
8
|
+
runTreeseedIntegratedDev
|
|
9
|
+
} from "./dev.js";
|
|
10
|
+
export {
|
|
11
|
+
TREESEED_PLATFORM_RESOURCE_KINDS,
|
|
12
|
+
buildTreeseedPlatformLayers,
|
|
13
|
+
createTreeseedIntegratedDevPlan,
|
|
14
|
+
resolveTreeseedPlatformResource,
|
|
15
|
+
runTreeseedIntegratedDev
|
|
16
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@treeseed/sdk/plugin-default';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createRailwayTreeseedApiServer } from './api/railway';
|