@treeseed/sdk 0.4.8 → 0.4.10
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 +1 -1
- package/dist/control-plane-client.d.ts +45 -0
- package/dist/control-plane-client.js +229 -0
- package/dist/control-plane.d.ts +94 -0
- package/dist/control-plane.js +125 -0
- package/dist/d1-store.d.ts +56 -1
- package/dist/d1-store.js +132 -0
- package/dist/dispatch.d.ts +4 -0
- package/dist/dispatch.js +180 -0
- package/dist/index.d.ts +14 -2
- package/dist/index.js +94 -4
- package/dist/operations/services/config-runtime.d.ts +10 -0
- package/dist/operations/services/config-runtime.js +62 -4
- package/dist/operations/services/deploy.d.ts +95 -3
- package/dist/operations/services/deploy.js +351 -10
- package/dist/operations/services/github-automation.d.ts +37 -1
- package/dist/operations/services/github-automation.js +71 -14
- package/dist/operations/services/project-platform.d.ts +835 -0
- package/dist/operations/services/project-platform.js +782 -0
- package/dist/operations/services/railway-deploy.d.ts +113 -18
- package/dist/operations/services/railway-deploy.js +357 -8
- package/dist/operations/services/runtime-tools.d.ts +25 -1
- package/dist/operations/services/runtime-tools.js +66 -5
- package/dist/operations/services/template-registry.d.ts +1 -1
- package/dist/operations/services/template-registry.js +17 -3
- package/dist/platform/books-data.d.ts +3 -4
- package/dist/platform/books-data.js +30 -4
- package/dist/platform/contracts.d.ts +56 -4
- package/dist/platform/deploy-config.js +109 -4
- package/dist/platform/deploy-runtime.d.ts +2 -0
- package/dist/platform/deploy-runtime.js +9 -1
- package/dist/platform/env.yaml +677 -0
- package/dist/platform/environment.js +57 -2
- package/dist/platform/plugin.d.ts +8 -0
- package/dist/platform/plugins/constants.d.ts +2 -0
- package/dist/platform/plugins/constants.js +2 -0
- package/dist/platform/plugins/runtime.d.ts +2 -0
- package/dist/platform/plugins/runtime.js +9 -1
- package/dist/platform/plugins.d.ts +1 -1
- package/dist/platform/plugins.js +4 -0
- package/dist/platform/published-content-pipeline.d.ts +84 -0
- package/dist/platform/published-content-pipeline.js +543 -0
- package/dist/platform/published-content.d.ts +223 -0
- package/dist/platform/published-content.js +588 -0
- package/dist/platform/tenant/runtime-config.d.ts +1 -1
- package/dist/platform/tenant/runtime-config.js +34 -1
- package/dist/platform/tenant-config.d.ts +2 -1
- package/dist/platform/tenant-config.js +17 -1
- package/dist/platform/utils/site-config-schema.js +104 -0
- package/dist/plugin-default.d.ts +2 -0
- package/dist/plugin-default.js +2 -0
- package/dist/remote.d.ts +65 -9
- package/dist/remote.js +104 -28
- package/dist/scripts/check-build-warnings.js +50 -0
- package/dist/scripts/config-treeseed.js +7 -0
- package/dist/scripts/tenant-workflow-action.js +71 -0
- package/dist/sdk-dispatch.d.ts +12 -0
- package/dist/sdk-dispatch.js +142 -0
- package/dist/sdk-types.d.ts +579 -7
- package/dist/sdk-types.js +53 -1
- package/dist/sdk.d.ts +17 -1
- package/dist/sdk.js +109 -0
- package/dist/stores/operational-store.d.ts +22 -2
- package/dist/stores/operational-store.js +235 -0
- package/dist/template-catalog.js +8 -1
- package/dist/treeseed/template-catalog/templates/starter-basic/template/treeseed.site.yaml +20 -0
- package/dist/types/cloudflare.d.ts +23 -0
- package/dist/workflow/operations.d.ts +12 -3
- package/dist/workflow/policy.d.ts +1 -1
- package/dist/workflow-state.js +2 -1
- package/package.json +7 -2
- package/templates/github/deploy.workflow.yml +442 -0
- package/templates/github/hosted-project.workflow.yml +77 -0
|
@@ -46,11 +46,13 @@ const TREESEED_DEFAULT_PROVIDER_SELECTIONS = {
|
|
|
46
46
|
},
|
|
47
47
|
deploy: "cloudflare",
|
|
48
48
|
content: {
|
|
49
|
+
runtime: "team_scoped_r2_overlay",
|
|
50
|
+
publish: "team_scoped_r2_overlay",
|
|
49
51
|
docs: "default"
|
|
50
52
|
},
|
|
51
53
|
site: "default"
|
|
52
54
|
};
|
|
53
|
-
const TRESEED_MANAGED_SERVICE_KEYS = ["api", "agents", "
|
|
55
|
+
const TRESEED_MANAGED_SERVICE_KEYS = ["api", "agents", "manager", "worker", "runner", "workdayStart", "workdayReport"];
|
|
54
56
|
const TRESEED_WORKSPACE_PACKAGE_DIRS = ["sdk", "core", "cli"];
|
|
55
57
|
const CLOUDFLARE_ACCOUNT_ID_PLACEHOLDER = "replace-with-cloudflare-account-id";
|
|
56
58
|
function parseServiceEnvironmentConfig(value) {
|
|
@@ -84,7 +86,8 @@ function parseManagedServiceConfig(value, label) {
|
|
|
84
86
|
serviceName: optionalString(railway.serviceName),
|
|
85
87
|
rootDir: optionalString(railway.rootDir),
|
|
86
88
|
buildCommand: optionalString(railway.buildCommand),
|
|
87
|
-
startCommand: optionalString(railway.startCommand)
|
|
89
|
+
startCommand: optionalString(railway.startCommand),
|
|
90
|
+
schedule: Array.isArray(railway.schedule) ? railway.schedule.map((entry) => optionalString(entry)).filter(Boolean) : optionalString(railway.schedule)
|
|
88
91
|
},
|
|
89
92
|
environments: {
|
|
90
93
|
local: parseServiceEnvironmentConfig(environments.local),
|
|
@@ -260,6 +263,25 @@ function optionalBoolean(value, label) {
|
|
|
260
263
|
}
|
|
261
264
|
return value;
|
|
262
265
|
}
|
|
266
|
+
function optionalPositiveNumber(value, label) {
|
|
267
|
+
if (value === void 0) {
|
|
268
|
+
return void 0;
|
|
269
|
+
}
|
|
270
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
271
|
+
throw new Error(`Invalid deploy config: expected ${label} to be a positive number when provided.`);
|
|
272
|
+
}
|
|
273
|
+
return value;
|
|
274
|
+
}
|
|
275
|
+
function optionalEnum(value, label, allowed) {
|
|
276
|
+
const normalized = optionalString(value);
|
|
277
|
+
if (!normalized) {
|
|
278
|
+
return void 0;
|
|
279
|
+
}
|
|
280
|
+
if (!allowed.includes(normalized)) {
|
|
281
|
+
throw new Error(`Invalid deploy config: expected ${label} to be one of ${allowed.join(", ")}.`);
|
|
282
|
+
}
|
|
283
|
+
return normalized;
|
|
284
|
+
}
|
|
263
285
|
function optionalRecord(value, label) {
|
|
264
286
|
if (value === void 0 || value === null) {
|
|
265
287
|
return void 0;
|
|
@@ -289,6 +311,9 @@ function parseFallbackDeployConfig(configPath) {
|
|
|
289
311
|
const parsed = parseYaml(readFileSync(configPath, "utf8")) ?? {};
|
|
290
312
|
const record = optionalRecord(parsed, "root") ?? {};
|
|
291
313
|
const cloudflare = optionalRecord(record.cloudflare, "cloudflare") ?? {};
|
|
314
|
+
const cloudflarePages = optionalRecord(cloudflare.pages, "cloudflare.pages") ?? {};
|
|
315
|
+
const cloudflareR2 = optionalRecord(cloudflare.r2, "cloudflare.r2") ?? {};
|
|
316
|
+
const hosting = optionalRecord(record.hosting, "hosting") ?? {};
|
|
292
317
|
const smtp = optionalRecord(record.smtp, "smtp") ?? {};
|
|
293
318
|
const turnstile = optionalRecord(record.turnstile, "turnstile") ?? {};
|
|
294
319
|
const agentProviders = optionalRecord(optionalRecord(record.providers, "providers")?.agents, "providers.agents") ?? {};
|
|
@@ -298,14 +323,39 @@ function parseFallbackDeployConfig(configPath) {
|
|
|
298
323
|
slug: expectString(record.slug, "slug"),
|
|
299
324
|
siteUrl: expectString(record.siteUrl, "siteUrl"),
|
|
300
325
|
contactEmail: expectString(record.contactEmail, "contactEmail"),
|
|
326
|
+
hosting: Object.keys(hosting).length === 0 ? void 0 : {
|
|
327
|
+
kind: optionalEnum(hosting.kind, "hosting.kind", [
|
|
328
|
+
"market_control_plane",
|
|
329
|
+
"hosted_project",
|
|
330
|
+
"self_hosted_project"
|
|
331
|
+
]) ?? "self_hosted_project",
|
|
332
|
+
registration: optionalEnum(hosting.registration, "hosting.registration", ["optional", "none"]) ?? "none",
|
|
333
|
+
marketBaseUrl: optionalString(hosting.marketBaseUrl),
|
|
334
|
+
teamId: optionalString(hosting.teamId),
|
|
335
|
+
projectId: optionalString(hosting.projectId)
|
|
336
|
+
},
|
|
301
337
|
cloudflare: {
|
|
302
338
|
accountId: optionalCloudflareAccountId(cloudflare.accountId) ?? optionalCloudflareAccountId(process.env.CLOUDFLARE_ACCOUNT_ID) ?? CLOUDFLARE_ACCOUNT_ID_PLACEHOLDER,
|
|
303
339
|
workerName: optionalString(cloudflare.workerName),
|
|
304
|
-
gatewayWorkerName: optionalString(cloudflare.gatewayWorkerName),
|
|
305
340
|
queueName: optionalString(cloudflare.queueName),
|
|
306
341
|
dlqName: optionalString(cloudflare.dlqName),
|
|
307
342
|
d1Binding: optionalString(cloudflare.d1Binding),
|
|
308
|
-
queueBinding: optionalString(cloudflare.queueBinding)
|
|
343
|
+
queueBinding: optionalString(cloudflare.queueBinding),
|
|
344
|
+
pages: cloudflare.pages === void 0 ? void 0 : {
|
|
345
|
+
projectName: optionalString(cloudflarePages.projectName) ?? optionalString(process.env.TREESEED_CLOUDFLARE_PAGES_PROJECT_NAME),
|
|
346
|
+
previewProjectName: optionalString(cloudflarePages.previewProjectName) ?? optionalString(process.env.TREESEED_CLOUDFLARE_PAGES_PREVIEW_PROJECT_NAME),
|
|
347
|
+
productionBranch: optionalString(cloudflarePages.productionBranch) ?? "main",
|
|
348
|
+
stagingBranch: optionalString(cloudflarePages.stagingBranch) ?? "staging",
|
|
349
|
+
buildOutputDir: optionalString(cloudflarePages.buildOutputDir)
|
|
350
|
+
},
|
|
351
|
+
r2: cloudflare.r2 === void 0 ? void 0 : {
|
|
352
|
+
binding: optionalString(cloudflareR2.binding) ?? optionalString(process.env.TREESEED_CONTENT_BUCKET_BINDING),
|
|
353
|
+
bucketName: optionalString(cloudflareR2.bucketName) ?? optionalString(process.env.TREESEED_CONTENT_BUCKET_NAME),
|
|
354
|
+
publicBaseUrl: optionalString(cloudflareR2.publicBaseUrl) ?? optionalString(process.env.TREESEED_CONTENT_PUBLIC_BASE_URL),
|
|
355
|
+
manifestKeyTemplate: optionalString(cloudflareR2.manifestKeyTemplate ?? cloudflareR2.manifestKey) ?? "teams/{teamId}/published/common.json",
|
|
356
|
+
previewRootTemplate: optionalString(cloudflareR2.previewRootTemplate ?? cloudflareR2.previewRoot) ?? "teams/{teamId}/previews",
|
|
357
|
+
previewTtlHours: optionalPositiveNumber(cloudflareR2.previewTtlHours, "cloudflare.r2.previewTtlHours") ?? 168
|
|
358
|
+
}
|
|
309
359
|
},
|
|
310
360
|
plugins: parsePluginReferences(record.plugins),
|
|
311
361
|
providers: {
|
|
@@ -320,7 +370,18 @@ function parseFallbackDeployConfig(configPath) {
|
|
|
320
370
|
},
|
|
321
371
|
deploy: expectString(record.providers?.deploy ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.deploy, "providers.deploy"),
|
|
322
372
|
content: {
|
|
323
|
-
|
|
373
|
+
runtime: expectString(
|
|
374
|
+
contentProviders.runtime ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.runtime,
|
|
375
|
+
"providers.content.runtime"
|
|
376
|
+
),
|
|
377
|
+
publish: expectString(
|
|
378
|
+
contentProviders.publish ?? contentProviders.runtime ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.publish,
|
|
379
|
+
"providers.content.publish"
|
|
380
|
+
),
|
|
381
|
+
docs: expectString(
|
|
382
|
+
contentProviders.docs ?? contentProviders.runtime ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.docs ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.runtime,
|
|
383
|
+
"providers.content.docs"
|
|
384
|
+
)
|
|
324
385
|
},
|
|
325
386
|
site: expectString(record.providers?.site ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.site, "providers.site")
|
|
326
387
|
},
|
|
@@ -83,7 +83,7 @@ export declare function serializeTemplateRegistryEntry(product: Pick<TemplatePro
|
|
|
83
83
|
templateApiVersion: number;
|
|
84
84
|
minCliVersion: string;
|
|
85
85
|
minCoreVersion: string | undefined;
|
|
86
|
-
fulfillmentMode: "git" | "packaged";
|
|
86
|
+
fulfillmentMode: "r2" | "git" | "packaged";
|
|
87
87
|
source: import("../../sdk-types.ts").SdkTemplateCatalogSource;
|
|
88
88
|
};
|
|
89
89
|
export declare function exportTemplateCatalogYaml(options?: TemplateCatalogOptions): Promise<string>;
|
|
@@ -97,9 +97,10 @@ function validateTemplatePlaceholders(definition) {
|
|
|
97
97
|
}
|
|
98
98
|
function normalizeTemplateProduct(remoteProduct) {
|
|
99
99
|
const artifactRoot = resolve(localTemplateArtifactsRoot, remoteProduct.id);
|
|
100
|
+
const source = remoteProduct.fulfillment.source;
|
|
100
101
|
return {
|
|
101
102
|
...remoteProduct,
|
|
102
|
-
contentPath: `${remoteProduct.
|
|
103
|
+
contentPath: source.kind === "git" ? `${source.repoUrl}#${remoteProduct.id}` : `r2://${source.bucket ?? "bucket"}/${source.objectKey}#${remoteProduct.id}`,
|
|
103
104
|
artifactRoot,
|
|
104
105
|
artifactManifestPath: resolve(artifactRoot, "template.config.json"),
|
|
105
106
|
templateRoot: resolve(artifactRoot, "template"),
|
|
@@ -111,7 +112,8 @@ function sanitizeCacheSegment(value) {
|
|
|
111
112
|
}
|
|
112
113
|
function resolveTemplateSourceCacheRoot(product, options) {
|
|
113
114
|
const cachePath = resolveTreeseedTemplateCatalogCachePath(options.cwd ?? process.cwd());
|
|
114
|
-
|
|
115
|
+
const sourceVersion = product.fulfillment.source.kind === "git" ? product.fulfillment.source.ref : product.fulfillment.source.version;
|
|
116
|
+
return resolve(dirname(cachePath), "templates", sanitizeCacheSegment(product.id), sanitizeCacheSegment(sourceVersion));
|
|
115
117
|
}
|
|
116
118
|
function runGit(commandArgs, cwd) {
|
|
117
119
|
const result = spawnSync("git", commandArgs, {
|
|
@@ -141,6 +143,18 @@ function materializeGitTemplateSource(product, options) {
|
|
|
141
143
|
templateRoot: resolve(artifactRoot, "template")
|
|
142
144
|
};
|
|
143
145
|
}
|
|
146
|
+
function materializeR2TemplateSource(product) {
|
|
147
|
+
if (existsSync(product.artifactManifestPath) && existsSync(product.templateRoot)) {
|
|
148
|
+
return {
|
|
149
|
+
artifactRoot: product.artifactRoot,
|
|
150
|
+
manifestPath: product.artifactManifestPath,
|
|
151
|
+
templateRoot: product.templateRoot
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Template ${product.id} uses an R2 fulfillment source (${product.fulfillment.source.objectKey}) but no packaged artifact is present in the local cache yet.`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
144
158
|
function resolveTemplateDefinitionPaths(product, options) {
|
|
145
159
|
if (existsSync(product.artifactManifestPath) && existsSync(product.templateRoot)) {
|
|
146
160
|
return {
|
|
@@ -149,7 +163,7 @@ function resolveTemplateDefinitionPaths(product, options) {
|
|
|
149
163
|
templateRoot: product.templateRoot
|
|
150
164
|
};
|
|
151
165
|
}
|
|
152
|
-
return materializeGitTemplateSource(product, options);
|
|
166
|
+
return product.fulfillment.source.kind === "git" ? materializeGitTemplateSource(product, options) : materializeR2TemplateSource(product);
|
|
153
167
|
}
|
|
154
168
|
function readTemplateCatalogCache(cachePath) {
|
|
155
169
|
if (!existsSync(cachePath)) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { TreeseedBookDefinition, TreeseedTenantConfig } from './contracts.ts';
|
|
2
|
-
interface DocsLibraryDownload {
|
|
2
|
+
export interface DocsLibraryDownload {
|
|
3
3
|
downloadFileName: string;
|
|
4
4
|
downloadHref: string;
|
|
5
5
|
downloadTitle: string;
|
|
6
6
|
}
|
|
7
|
-
interface
|
|
7
|
+
export interface TreeseedBookRuntime {
|
|
8
8
|
BOOKS: TreeseedBookDefinition[];
|
|
9
9
|
BOOKS_LINK: {
|
|
10
10
|
label: string;
|
|
@@ -19,11 +19,10 @@ export declare function buildTenantBookRuntime(tenantConfig: Pick<TreeseedTenant
|
|
|
19
19
|
projectRoot?: string;
|
|
20
20
|
docsHomePath?: string;
|
|
21
21
|
docsLibraryDownload?: DocsLibraryDownload;
|
|
22
|
-
}):
|
|
22
|
+
}): TreeseedBookRuntime;
|
|
23
23
|
export declare const BOOKS: TreeseedBookDefinition[], BOOKS_LINK: {
|
|
24
24
|
label: string;
|
|
25
25
|
link: string;
|
|
26
26
|
}, TREESEED_LINKS: {
|
|
27
27
|
home: string;
|
|
28
28
|
}, TREESEED_LIBRARY_DOWNLOAD: DocsLibraryDownload;
|
|
29
|
-
export {};
|
|
@@ -3,6 +3,25 @@ import path from "node:path";
|
|
|
3
3
|
import { parse as parseYaml } from "yaml";
|
|
4
4
|
import { getTenantContentRoot } from "./tenant-config.js";
|
|
5
5
|
import { RUNTIME_PROJECT_ROOT, RUNTIME_TENANT } from "./tenant/runtime-config.js";
|
|
6
|
+
function fallbackTenantBookRuntime(options = {}) {
|
|
7
|
+
const docsHomePath = options.docsHomePath ?? "/knowledge/";
|
|
8
|
+
const docsLibraryDownload = options.docsLibraryDownload ?? {
|
|
9
|
+
downloadFileName: "treeseed-knowledge.md",
|
|
10
|
+
downloadHref: "/books/treeseed-knowledge.md",
|
|
11
|
+
downloadTitle: "TreeSeed Knowledge Library"
|
|
12
|
+
};
|
|
13
|
+
return {
|
|
14
|
+
BOOKS: [],
|
|
15
|
+
BOOKS_LINK: {
|
|
16
|
+
label: "Books",
|
|
17
|
+
link: docsHomePath
|
|
18
|
+
},
|
|
19
|
+
TREESEED_LINKS: {
|
|
20
|
+
home: docsHomePath
|
|
21
|
+
},
|
|
22
|
+
TREESEED_LIBRARY_DOWNLOAD: docsLibraryDownload
|
|
23
|
+
};
|
|
24
|
+
}
|
|
6
25
|
function sortPaths(paths) {
|
|
7
26
|
return [...paths].sort((left, right) => left.localeCompare(right, void 0, { numeric: true, sensitivity: "base" }));
|
|
8
27
|
}
|
|
@@ -64,14 +83,21 @@ function buildTenantBookRuntime(tenantConfig, options = {}) {
|
|
|
64
83
|
TREESEED_LIBRARY_DOWNLOAD: docsLibraryDownload
|
|
65
84
|
};
|
|
66
85
|
}
|
|
67
|
-
const runtime =
|
|
68
|
-
|
|
69
|
-
docsLibraryDownload: {
|
|
86
|
+
const runtime = (() => {
|
|
87
|
+
const docsLibraryDownload = {
|
|
70
88
|
downloadFileName: "treeseed-knowledge.md",
|
|
71
89
|
downloadHref: "/books/treeseed-knowledge.md",
|
|
72
90
|
downloadTitle: "TreeSeed Knowledge Library"
|
|
91
|
+
};
|
|
92
|
+
try {
|
|
93
|
+
return buildTenantBookRuntime(RUNTIME_TENANT, {
|
|
94
|
+
projectRoot: RUNTIME_PROJECT_ROOT,
|
|
95
|
+
docsLibraryDownload
|
|
96
|
+
});
|
|
97
|
+
} catch {
|
|
98
|
+
return fallbackTenantBookRuntime({ docsLibraryDownload });
|
|
73
99
|
}
|
|
74
|
-
});
|
|
100
|
+
})();
|
|
75
101
|
const { BOOKS, BOOKS_LINK, TREESEED_LINKS, TREESEED_LIBRARY_DOWNLOAD } = runtime;
|
|
76
102
|
export {
|
|
77
103
|
BOOKS,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type TreeseedFeatureName = 'docs' | 'books' | 'notes' | 'questions' | 'objectives' | 'agents' | 'forms';
|
|
2
|
-
export type TreeseedContentCollection = 'pages' | 'notes' | 'questions' | 'objectives' | 'people' | 'agents' | 'books' | 'docs';
|
|
2
|
+
export type TreeseedContentCollection = 'pages' | 'notes' | 'questions' | 'objectives' | 'people' | 'agents' | 'books' | 'docs' | 'templates' | 'knowledge_packs' | 'workdays';
|
|
3
3
|
export interface TreeseedFeatureModules {
|
|
4
4
|
docs?: boolean;
|
|
5
5
|
books?: boolean;
|
|
@@ -19,6 +19,20 @@ export interface TreeseedContentMap {
|
|
|
19
19
|
agents: string;
|
|
20
20
|
books: string;
|
|
21
21
|
docs: string;
|
|
22
|
+
templates?: string;
|
|
23
|
+
knowledge_packs?: string;
|
|
24
|
+
workdays?: string;
|
|
25
|
+
[key: string]: string | undefined;
|
|
26
|
+
}
|
|
27
|
+
export interface TreeseedTenantSiteModelConfig {
|
|
28
|
+
/**
|
|
29
|
+
* Controls whether this content model should be rendered by the site runtime.
|
|
30
|
+
* Content remains managed in Git and available through SDK/content pipelines.
|
|
31
|
+
*/
|
|
32
|
+
rendered?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface TreeseedTenantSiteConfig {
|
|
35
|
+
models?: Partial<Record<TreeseedContentCollection, TreeseedTenantSiteModelConfig>>;
|
|
22
36
|
}
|
|
23
37
|
export interface TreeseedBookDefinition {
|
|
24
38
|
order: number;
|
|
@@ -83,7 +97,7 @@ export interface TreeseedPluginReference {
|
|
|
83
97
|
enabled?: boolean;
|
|
84
98
|
config?: Record<string, unknown>;
|
|
85
99
|
}
|
|
86
|
-
export type TreeseedPlatformSurfaceName = 'web' | 'api' |
|
|
100
|
+
export type TreeseedPlatformSurfaceName = 'web' | 'api' | (string & {});
|
|
87
101
|
export type TreeseedPlatformResourceKind = 'pages' | 'styles' | 'components' | 'routes' | 'middleware' | 'handlers' | 'config';
|
|
88
102
|
export interface TreeseedPlatformLayerDefinition {
|
|
89
103
|
root: string;
|
|
@@ -105,11 +119,38 @@ export interface TreeseedPlatformSurfaceConfig {
|
|
|
105
119
|
publicBaseUrl?: string;
|
|
106
120
|
localBaseUrl?: string;
|
|
107
121
|
}
|
|
122
|
+
export interface TreeseedCloudflareR2Config {
|
|
123
|
+
binding?: string;
|
|
124
|
+
bucketName?: string;
|
|
125
|
+
publicBaseUrl?: string;
|
|
126
|
+
manifestKeyTemplate?: string;
|
|
127
|
+
previewRootTemplate?: string;
|
|
128
|
+
previewTtlHours?: number;
|
|
129
|
+
}
|
|
130
|
+
export interface TreeseedCloudflarePagesConfig {
|
|
131
|
+
projectName?: string;
|
|
132
|
+
previewProjectName?: string;
|
|
133
|
+
productionBranch?: string;
|
|
134
|
+
stagingBranch?: string;
|
|
135
|
+
buildOutputDir?: string;
|
|
136
|
+
}
|
|
137
|
+
export type TreeseedHostingKind = 'market_control_plane' | 'hosted_project' | 'self_hosted_project';
|
|
138
|
+
export type TreeseedHostingRegistration = 'optional' | 'none';
|
|
139
|
+
export interface TreeseedHostingConfig {
|
|
140
|
+
kind: TreeseedHostingKind;
|
|
141
|
+
registration?: TreeseedHostingRegistration;
|
|
142
|
+
marketBaseUrl?: string;
|
|
143
|
+
teamId?: string;
|
|
144
|
+
projectId?: string;
|
|
145
|
+
}
|
|
108
146
|
export interface TreeseedManagedServiceEnvironmentConfig {
|
|
109
147
|
baseUrl?: string;
|
|
110
148
|
domain?: string;
|
|
111
149
|
railwayEnvironment?: string;
|
|
112
150
|
}
|
|
151
|
+
export interface TreeseedManagedServiceCloudflareConfig {
|
|
152
|
+
workerName?: string;
|
|
153
|
+
}
|
|
113
154
|
export interface TreeseedManagedServiceRailwayConfig {
|
|
114
155
|
projectId?: string;
|
|
115
156
|
projectName?: string;
|
|
@@ -118,12 +159,14 @@ export interface TreeseedManagedServiceRailwayConfig {
|
|
|
118
159
|
rootDir?: string;
|
|
119
160
|
buildCommand?: string;
|
|
120
161
|
startCommand?: string;
|
|
162
|
+
schedule?: string | string[];
|
|
121
163
|
}
|
|
122
164
|
export interface TreeseedManagedServiceConfig {
|
|
123
165
|
enabled?: boolean;
|
|
124
166
|
provider?: string;
|
|
125
167
|
rootDir?: string;
|
|
126
168
|
publicBaseUrl?: string;
|
|
169
|
+
cloudflare?: TreeseedManagedServiceCloudflareConfig;
|
|
127
170
|
railway?: TreeseedManagedServiceRailwayConfig;
|
|
128
171
|
environments?: Partial<Record<'local' | 'staging' | 'prod', TreeseedManagedServiceEnvironmentConfig>>;
|
|
129
172
|
}
|
|
@@ -135,7 +178,6 @@ export interface TreeseedManagedServicesConfig {
|
|
|
135
178
|
export interface TreeseedPlatformSurfacesConfig {
|
|
136
179
|
web?: TreeseedPlatformSurfaceConfig;
|
|
137
180
|
api?: TreeseedPlatformSurfaceConfig;
|
|
138
|
-
gateway?: TreeseedPlatformSurfaceConfig;
|
|
139
181
|
[key: string]: TreeseedPlatformSurfaceConfig | undefined;
|
|
140
182
|
}
|
|
141
183
|
export interface TreeseedProviderSelections {
|
|
@@ -151,7 +193,9 @@ export interface TreeseedProviderSelections {
|
|
|
151
193
|
};
|
|
152
194
|
deploy: string;
|
|
153
195
|
content?: {
|
|
154
|
-
|
|
196
|
+
runtime: string;
|
|
197
|
+
publish: string;
|
|
198
|
+
docs?: string;
|
|
155
199
|
};
|
|
156
200
|
site?: string;
|
|
157
201
|
}
|
|
@@ -164,9 +208,16 @@ export interface TreeseedDeployConfig {
|
|
|
164
208
|
slug: string;
|
|
165
209
|
siteUrl: string;
|
|
166
210
|
contactEmail: string;
|
|
211
|
+
hosting?: TreeseedHostingConfig;
|
|
167
212
|
cloudflare: {
|
|
168
213
|
accountId: string;
|
|
169
214
|
workerName?: string;
|
|
215
|
+
queueName?: string;
|
|
216
|
+
dlqName?: string;
|
|
217
|
+
d1Binding?: string;
|
|
218
|
+
queueBinding?: string;
|
|
219
|
+
pages?: TreeseedCloudflarePagesConfig;
|
|
220
|
+
r2?: TreeseedCloudflareR2Config;
|
|
170
221
|
};
|
|
171
222
|
plugins: TreeseedPluginReference[];
|
|
172
223
|
providers: TreeseedProviderSelections;
|
|
@@ -185,5 +236,6 @@ export interface TreeseedTenantConfig {
|
|
|
185
236
|
siteConfigPath: string;
|
|
186
237
|
content: TreeseedContentMap;
|
|
187
238
|
features: TreeseedFeatureModules;
|
|
239
|
+
site?: TreeseedTenantSiteConfig;
|
|
188
240
|
overrides?: TreeseedTenantOverrides;
|
|
189
241
|
}
|
|
@@ -11,9 +11,35 @@ const deployConfigFieldAliases = {
|
|
|
11
11
|
siteUrl: { key: "siteUrl", aliases: ["site_url"] },
|
|
12
12
|
contactEmail: { key: "contactEmail", aliases: ["contact_email"] }
|
|
13
13
|
};
|
|
14
|
+
const hostingFieldAliases = {
|
|
15
|
+
kind: { key: "kind", aliases: ["kind"] },
|
|
16
|
+
registration: { key: "registration", aliases: ["registration"] },
|
|
17
|
+
marketBaseUrl: { key: "marketBaseUrl", aliases: ["market_base_url"] },
|
|
18
|
+
teamId: { key: "teamId", aliases: ["team_id"] },
|
|
19
|
+
projectId: { key: "projectId", aliases: ["project_id"] }
|
|
20
|
+
};
|
|
14
21
|
const cloudflareFieldAliases = {
|
|
15
22
|
accountId: { key: "accountId", aliases: ["account_id"] },
|
|
16
|
-
workerName: { key: "workerName", aliases: ["worker_name"] }
|
|
23
|
+
workerName: { key: "workerName", aliases: ["worker_name"] },
|
|
24
|
+
queueName: { key: "queueName", aliases: ["queue_name"] },
|
|
25
|
+
dlqName: { key: "dlqName", aliases: ["dlq_name"] },
|
|
26
|
+
d1Binding: { key: "d1Binding", aliases: ["d1_binding"] },
|
|
27
|
+
queueBinding: { key: "queueBinding", aliases: ["queue_binding"] }
|
|
28
|
+
};
|
|
29
|
+
const cloudflarePagesFieldAliases = {
|
|
30
|
+
projectName: { key: "projectName", aliases: ["project_name"] },
|
|
31
|
+
previewProjectName: { key: "previewProjectName", aliases: ["preview_project_name"] },
|
|
32
|
+
productionBranch: { key: "productionBranch", aliases: ["production_branch"] },
|
|
33
|
+
stagingBranch: { key: "stagingBranch", aliases: ["staging_branch"] },
|
|
34
|
+
buildOutputDir: { key: "buildOutputDir", aliases: ["build_output_dir"] }
|
|
35
|
+
};
|
|
36
|
+
const cloudflareR2FieldAliases = {
|
|
37
|
+
binding: { key: "binding", aliases: ["binding"] },
|
|
38
|
+
bucketName: { key: "bucketName", aliases: ["bucket_name"] },
|
|
39
|
+
publicBaseUrl: { key: "publicBaseUrl", aliases: ["public_base_url"] },
|
|
40
|
+
manifestKeyTemplate: { key: "manifestKeyTemplate", aliases: ["manifest_key_template", "manifest_key"] },
|
|
41
|
+
previewRootTemplate: { key: "previewRootTemplate", aliases: ["preview_root_template", "preview_root"] },
|
|
42
|
+
previewTtlHours: { key: "previewTtlHours", aliases: ["preview_ttl_hours"] }
|
|
17
43
|
};
|
|
18
44
|
const CLOUDFLARE_ACCOUNT_ID_PLACEHOLDER = "replace-with-cloudflare-account-id";
|
|
19
45
|
function expectString(value, label) {
|
|
@@ -32,6 +58,28 @@ function optionalCloudflareAccountId(value) {
|
|
|
32
58
|
const accountId = optionalString(value);
|
|
33
59
|
return accountId === CLOUDFLARE_ACCOUNT_ID_PLACEHOLDER ? void 0 : accountId;
|
|
34
60
|
}
|
|
61
|
+
function optionalPositiveNumber(value, label) {
|
|
62
|
+
if (value === void 0) {
|
|
63
|
+
return void 0;
|
|
64
|
+
}
|
|
65
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
66
|
+
throw new Error(`Invalid deploy config: expected ${label} to be a positive number when provided.`);
|
|
67
|
+
}
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
function optionalEnum(value, label, allowed) {
|
|
71
|
+
if (value === void 0) {
|
|
72
|
+
return void 0;
|
|
73
|
+
}
|
|
74
|
+
const normalized = optionalString(value);
|
|
75
|
+
if (!normalized) {
|
|
76
|
+
return void 0;
|
|
77
|
+
}
|
|
78
|
+
if (!allowed.includes(normalized)) {
|
|
79
|
+
throw new Error(`Invalid deploy config: expected ${label} to be one of ${allowed.join(", ")}.`);
|
|
80
|
+
}
|
|
81
|
+
return normalized;
|
|
82
|
+
}
|
|
35
83
|
function optionalBoolean(value, label) {
|
|
36
84
|
if (value === void 0) {
|
|
37
85
|
return void 0;
|
|
@@ -75,6 +123,26 @@ function parsePluginReferences(value) {
|
|
|
75
123
|
};
|
|
76
124
|
});
|
|
77
125
|
}
|
|
126
|
+
function parseHostingConfig(value) {
|
|
127
|
+
const record = normalizeAliasedRecord(
|
|
128
|
+
hostingFieldAliases,
|
|
129
|
+
optionalRecord(value, "hosting") ?? {}
|
|
130
|
+
);
|
|
131
|
+
if (!value || Object.keys(record).length === 0) {
|
|
132
|
+
return void 0;
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
kind: optionalEnum(record.kind, "hosting.kind", [
|
|
136
|
+
"market_control_plane",
|
|
137
|
+
"hosted_project",
|
|
138
|
+
"self_hosted_project"
|
|
139
|
+
]) ?? "self_hosted_project",
|
|
140
|
+
registration: optionalEnum(record.registration, "hosting.registration", ["optional", "none"]) ?? "none",
|
|
141
|
+
marketBaseUrl: optionalString(record.marketBaseUrl),
|
|
142
|
+
teamId: optionalString(record.teamId),
|
|
143
|
+
projectId: optionalString(record.projectId)
|
|
144
|
+
};
|
|
145
|
+
}
|
|
78
146
|
function parseProviderSelections(value) {
|
|
79
147
|
const record = optionalRecord(value, "providers");
|
|
80
148
|
if (!record) {
|
|
@@ -113,8 +181,16 @@ function parseProviderSelections(value) {
|
|
|
113
181
|
},
|
|
114
182
|
deploy: expectString(record.deploy ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.deploy, "providers.deploy"),
|
|
115
183
|
content: {
|
|
184
|
+
runtime: expectString(
|
|
185
|
+
contentProviders.runtime ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.runtime,
|
|
186
|
+
"providers.content.runtime"
|
|
187
|
+
),
|
|
188
|
+
publish: expectString(
|
|
189
|
+
contentProviders.publish ?? contentProviders.runtime ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.publish,
|
|
190
|
+
"providers.content.publish"
|
|
191
|
+
),
|
|
116
192
|
docs: expectString(
|
|
117
|
-
contentProviders.docs ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.docs,
|
|
193
|
+
contentProviders.docs ?? contentProviders.runtime ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.docs ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.runtime,
|
|
118
194
|
"providers.content.docs"
|
|
119
195
|
)
|
|
120
196
|
},
|
|
@@ -148,7 +224,8 @@ function parseManagedServiceConfig(value, label) {
|
|
|
148
224
|
serviceName: optionalString(railway.serviceName),
|
|
149
225
|
rootDir: optionalString(railway.rootDir),
|
|
150
226
|
buildCommand: optionalString(railway.buildCommand),
|
|
151
|
-
startCommand: optionalString(railway.startCommand)
|
|
227
|
+
startCommand: optionalString(railway.startCommand),
|
|
228
|
+
schedule: Array.isArray(railway.schedule) ? railway.schedule.map((entry) => optionalString(entry)).filter(Boolean) : optionalString(railway.schedule)
|
|
152
229
|
},
|
|
153
230
|
environments: {
|
|
154
231
|
local: parseServiceEnvironmentConfig(environments.local, `${label}.environments.local`),
|
|
@@ -213,6 +290,14 @@ function parseDeployConfig(raw) {
|
|
|
213
290
|
cloudflareFieldAliases,
|
|
214
291
|
optionalRecord(parsed.cloudflare, "cloudflare") ?? {}
|
|
215
292
|
);
|
|
293
|
+
const cloudflarePages = normalizeAliasedRecord(
|
|
294
|
+
cloudflarePagesFieldAliases,
|
|
295
|
+
optionalRecord(cloudflare.pages, "cloudflare.pages") ?? {}
|
|
296
|
+
);
|
|
297
|
+
const cloudflareR2 = normalizeAliasedRecord(
|
|
298
|
+
cloudflareR2FieldAliases,
|
|
299
|
+
optionalRecord(cloudflare.r2, "cloudflare.r2") ?? {}
|
|
300
|
+
);
|
|
216
301
|
const smtp = optionalRecord(parsed.smtp, "smtp") ?? {};
|
|
217
302
|
const turnstile = optionalRecord(parsed.turnstile, "turnstile") ?? {};
|
|
218
303
|
optionalBoolean(turnstile.enabled, "turnstile.enabled");
|
|
@@ -221,9 +306,29 @@ function parseDeployConfig(raw) {
|
|
|
221
306
|
slug: expectString(parsed.slug, "slug"),
|
|
222
307
|
siteUrl: expectString(parsed.siteUrl, "siteUrl"),
|
|
223
308
|
contactEmail: expectString(parsed.contactEmail, "contactEmail"),
|
|
309
|
+
hosting: parseHostingConfig(parsed.hosting),
|
|
224
310
|
cloudflare: {
|
|
225
311
|
accountId: optionalCloudflareAccountId(cloudflare.accountId) ?? optionalCloudflareAccountId(process.env.CLOUDFLARE_ACCOUNT_ID) ?? CLOUDFLARE_ACCOUNT_ID_PLACEHOLDER,
|
|
226
|
-
workerName: optionalString(cloudflare.workerName)
|
|
312
|
+
workerName: optionalString(cloudflare.workerName),
|
|
313
|
+
queueName: optionalString(cloudflare.queueName),
|
|
314
|
+
dlqName: optionalString(cloudflare.dlqName),
|
|
315
|
+
d1Binding: optionalString(cloudflare.d1Binding),
|
|
316
|
+
queueBinding: optionalString(cloudflare.queueBinding),
|
|
317
|
+
pages: cloudflare.pages === void 0 ? void 0 : {
|
|
318
|
+
projectName: optionalString(cloudflarePages.projectName) ?? optionalString(process.env.TREESEED_CLOUDFLARE_PAGES_PROJECT_NAME),
|
|
319
|
+
previewProjectName: optionalString(cloudflarePages.previewProjectName) ?? optionalString(process.env.TREESEED_CLOUDFLARE_PAGES_PREVIEW_PROJECT_NAME),
|
|
320
|
+
productionBranch: optionalString(cloudflarePages.productionBranch) ?? "main",
|
|
321
|
+
stagingBranch: optionalString(cloudflarePages.stagingBranch) ?? "staging",
|
|
322
|
+
buildOutputDir: optionalString(cloudflarePages.buildOutputDir)
|
|
323
|
+
},
|
|
324
|
+
r2: cloudflare.r2 === void 0 ? void 0 : {
|
|
325
|
+
binding: optionalString(cloudflareR2.binding) ?? optionalString(process.env.TREESEED_CONTENT_BUCKET_BINDING),
|
|
326
|
+
bucketName: optionalString(cloudflareR2.bucketName) ?? optionalString(process.env.TREESEED_CONTENT_BUCKET_NAME),
|
|
327
|
+
publicBaseUrl: optionalString(cloudflareR2.publicBaseUrl) ?? optionalString(process.env.TREESEED_CONTENT_PUBLIC_BASE_URL),
|
|
328
|
+
manifestKeyTemplate: optionalString(cloudflareR2.manifestKeyTemplate) ?? "teams/{teamId}/published/common.json",
|
|
329
|
+
previewRootTemplate: optionalString(cloudflareR2.previewRootTemplate) ?? "teams/{teamId}/previews",
|
|
330
|
+
previewTtlHours: optionalPositiveNumber(cloudflareR2.previewTtlHours, "cloudflare.r2.previewTtlHours") ?? 168
|
|
331
|
+
}
|
|
227
332
|
},
|
|
228
333
|
plugins: parsePluginReferences(parsed.plugins),
|
|
229
334
|
providers: parseProviderSelections(parsed.providers),
|
|
@@ -12,6 +12,8 @@ export declare function getTreeseedAgentProviderSelections(): {
|
|
|
12
12
|
research: string;
|
|
13
13
|
};
|
|
14
14
|
export declare function getTreeseedDeployProvider(): string;
|
|
15
|
+
export declare function getTreeseedContentRuntimeProvider(): string;
|
|
16
|
+
export declare function getTreeseedContentPublishProvider(): string;
|
|
15
17
|
export declare function getTreeseedDocsProvider(): string;
|
|
16
18
|
export declare function getTreeseedSiteProvider(): string;
|
|
17
19
|
export declare function isTreeseedSmtpEnabled(): boolean;
|
|
@@ -52,8 +52,14 @@ function getTreeseedAgentProviderSelections() {
|
|
|
52
52
|
function getTreeseedDeployProvider() {
|
|
53
53
|
return getTreeseedDeployConfig().providers?.deploy ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.deploy;
|
|
54
54
|
}
|
|
55
|
+
function getTreeseedContentRuntimeProvider() {
|
|
56
|
+
return getTreeseedDeployConfig().providers?.content?.runtime ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.runtime;
|
|
57
|
+
}
|
|
58
|
+
function getTreeseedContentPublishProvider() {
|
|
59
|
+
return getTreeseedDeployConfig().providers?.content?.publish ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.publish;
|
|
60
|
+
}
|
|
55
61
|
function getTreeseedDocsProvider() {
|
|
56
|
-
return getTreeseedDeployConfig().providers?.content?.docs ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.docs;
|
|
62
|
+
return getTreeseedDeployConfig().providers?.content?.docs ?? getTreeseedDeployConfig().providers?.content?.runtime ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.docs ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.runtime;
|
|
57
63
|
}
|
|
58
64
|
function getTreeseedSiteProvider() {
|
|
59
65
|
return getTreeseedDeployConfig().providers?.site ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.site;
|
|
@@ -66,6 +72,8 @@ function isTreeseedTurnstileEnabled() {
|
|
|
66
72
|
}
|
|
67
73
|
export {
|
|
68
74
|
getTreeseedAgentProviderSelections,
|
|
75
|
+
getTreeseedContentPublishProvider,
|
|
76
|
+
getTreeseedContentRuntimeProvider,
|
|
69
77
|
getTreeseedDeployConfig,
|
|
70
78
|
getTreeseedDeployProvider,
|
|
71
79
|
getTreeseedDocsProvider,
|