@treeseed/core 0.1.2 → 0.3.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.
@@ -1,130 +1,2 @@
1
- import type { TreeseedDeployConfig, TreeseedTenantConfig } from './contracts';
2
- import { type LoadedTreeseedPluginEntry } from './plugins/runtime';
3
- export declare const TREESEED_ENVIRONMENT_SCOPES: readonly ["local", "staging", "prod"];
4
- export declare const TREESEED_ENVIRONMENT_REQUIREMENTS: readonly ["required", "conditional", "optional"];
5
- export declare const TREESEED_ENVIRONMENT_TARGETS: readonly ["local-file", "wrangler-dev-vars", "github-secret", "github-variable", "cloudflare-secret", "cloudflare-var", "config-file"];
6
- export declare const TREESEED_ENVIRONMENT_PURPOSES: readonly ["dev", "save", "deploy", "destroy", "config"];
7
- export declare const TREESEED_ENVIRONMENT_SENSITIVITY: readonly ["secret", "plain", "derived"];
8
- export type TreeseedEnvironmentScope = (typeof TREESEED_ENVIRONMENT_SCOPES)[number];
9
- export type TreeseedEnvironmentRequirement = (typeof TREESEED_ENVIRONMENT_REQUIREMENTS)[number];
10
- export type TreeseedEnvironmentTarget = (typeof TREESEED_ENVIRONMENT_TARGETS)[number];
11
- export type TreeseedEnvironmentPurpose = (typeof TREESEED_ENVIRONMENT_PURPOSES)[number];
12
- export type TreeseedEnvironmentSensitivity = (typeof TREESEED_ENVIRONMENT_SENSITIVITY)[number];
13
- export type TreeseedEnvironmentValidation = {
14
- kind: 'string' | 'nonempty' | 'boolean' | 'number' | 'url' | 'email';
15
- } | {
16
- kind: 'enum';
17
- values: string[];
18
- };
19
- export type TreeseedEnvironmentValueResolver = string | ((context: TreeseedEnvironmentContext, scope: TreeseedEnvironmentScope) => string);
20
- export type TreeseedMachineSecretPayload = {
21
- algorithm: 'aes-256-gcm';
22
- iv: string;
23
- tag: string;
24
- ciphertext: string;
25
- };
26
- export type TreeseedMachineConfig = {
27
- version: number;
28
- project: {
29
- tenantRoot: string;
30
- tenantId: string;
31
- slug: string;
32
- name: string;
33
- siteUrl: string;
34
- overlayPath?: string;
35
- };
36
- settings: {
37
- sync: {
38
- github: boolean;
39
- cloudflare: boolean;
40
- };
41
- };
42
- environments: Record<TreeseedEnvironmentScope, {
43
- values: Record<string, string>;
44
- secrets: Record<string, TreeseedMachineSecretPayload>;
45
- }>;
46
- };
47
- export type TreeseedEnvironmentContext = {
48
- deployConfig: TreeseedDeployConfig;
49
- tenantConfig?: TreeseedTenantConfig;
50
- plugins: LoadedTreeseedPluginEntry[];
51
- tenantRoot: string;
52
- };
53
- export type TreeseedEnvironmentEntry = {
54
- id: string;
55
- label: string;
56
- group: string;
57
- description: string;
58
- howToGet: string;
59
- sensitivity: TreeseedEnvironmentSensitivity;
60
- targets: TreeseedEnvironmentTarget[];
61
- scopes: TreeseedEnvironmentScope[];
62
- requirement: TreeseedEnvironmentRequirement;
63
- purposes: TreeseedEnvironmentPurpose[];
64
- validation?: TreeseedEnvironmentValidation;
65
- sourcePriority?: string[];
66
- defaultValue?: TreeseedEnvironmentValueResolver;
67
- localDefaultValue?: TreeseedEnvironmentValueResolver;
68
- isRelevant?: (context: TreeseedEnvironmentContext, scope: TreeseedEnvironmentScope, purpose?: TreeseedEnvironmentPurpose) => boolean;
69
- requiredWhen?: (context: TreeseedEnvironmentContext, scope: TreeseedEnvironmentScope, purpose?: TreeseedEnvironmentPurpose) => boolean;
70
- };
71
- export type TreeseedEnvironmentEntryYaml = Omit<TreeseedEnvironmentEntry, 'id' | 'defaultValue' | 'localDefaultValue' | 'isRelevant' | 'requiredWhen'> & {
72
- defaultValueRef?: string;
73
- localDefaultValueRef?: string;
74
- relevanceRef?: string;
75
- requiredWhenRef?: string;
76
- };
77
- export type TreeseedEnvironmentEntryOverride = Partial<Omit<TreeseedEnvironmentEntryYaml, 'id'>> & {
78
- id?: string;
79
- };
80
- export type TreeseedEnvironmentRegistryOverlay = {
81
- entries?: Record<string, TreeseedEnvironmentEntryOverride>;
82
- };
83
- export type TreeseedResolvedEnvironmentRegistry = {
84
- context: TreeseedEnvironmentContext;
85
- entries: TreeseedEnvironmentEntry[];
86
- };
87
- export type TreeseedEnvironmentValidationProblem = {
88
- id: string;
89
- label: string;
90
- reason: 'missing' | 'invalid';
91
- message: string;
92
- entry: TreeseedEnvironmentEntry;
93
- };
94
- export type TreeseedEnvironmentValidationResult = {
95
- ok: boolean;
96
- entries: TreeseedEnvironmentEntry[];
97
- required: TreeseedEnvironmentEntry[];
98
- missing: TreeseedEnvironmentValidationProblem[];
99
- invalid: TreeseedEnvironmentValidationProblem[];
100
- };
101
- export declare function loadTreeseedEnvironmentOverlay(tenantRoot: string): {
102
- path: string;
103
- overlay: TreeseedEnvironmentRegistryOverlay;
104
- };
105
- export declare function resolveTreeseedEnvironmentContext(options?: {
106
- deployConfig?: TreeseedDeployConfig;
107
- tenantConfig?: TreeseedTenantConfig;
108
- plugins?: LoadedTreeseedPluginEntry[];
109
- }): TreeseedEnvironmentContext;
110
- export declare function resolveTreeseedEnvironmentRegistry(options?: {
111
- deployConfig?: TreeseedDeployConfig;
112
- tenantConfig?: TreeseedTenantConfig;
113
- plugins?: LoadedTreeseedPluginEntry[];
114
- }): TreeseedResolvedEnvironmentRegistry;
115
- export declare function isTreeseedEnvironmentEntryRelevant(entry: TreeseedEnvironmentEntry, context: TreeseedEnvironmentContext, scope: TreeseedEnvironmentScope, purpose?: TreeseedEnvironmentPurpose): boolean;
116
- export declare function getTreeseedEnvironmentSuggestedValues(options: {
117
- scope: TreeseedEnvironmentScope;
118
- purpose?: TreeseedEnvironmentPurpose;
119
- deployConfig?: TreeseedDeployConfig;
120
- tenantConfig?: TreeseedTenantConfig;
121
- plugins?: LoadedTreeseedPluginEntry[];
122
- }): Record<string, string>;
123
- export declare function validateTreeseedEnvironmentValues(options: {
124
- values: Record<string, string | undefined>;
125
- scope: TreeseedEnvironmentScope;
126
- purpose: TreeseedEnvironmentPurpose;
127
- deployConfig?: TreeseedDeployConfig;
128
- tenantConfig?: TreeseedTenantConfig;
129
- plugins?: LoadedTreeseedPluginEntry[];
130
- }): TreeseedEnvironmentValidationResult;
1
+ export { getTreeseedEnvironmentSuggestedValues, isTreeseedEnvironmentEntryRelevant, loadTreeseedEnvironmentOverlay, resolveTreeseedEnvironmentContext, resolveTreeseedEnvironmentRegistry, TREESEED_ENVIRONMENT_PURPOSES, TREESEED_ENVIRONMENT_REQUIREMENTS, TREESEED_ENVIRONMENT_SCOPES, TREESEED_ENVIRONMENT_SENSITIVITY, TREESEED_ENVIRONMENT_TARGETS, validateTreeseedEnvironmentValues, } from '@treeseed/sdk/platform/environment';
2
+ export type * from '@treeseed/sdk/platform/environment';
@@ -1,314 +1,16 @@
1
- import { randomBytes } from "node:crypto";
2
- import { existsSync, readFileSync } from "node:fs";
3
- import { dirname, resolve } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { parse as parseYaml } from "yaml";
6
- import { loadTreeseedDeployConfig } from "./deploy/config.js";
7
- import { loadTreeseedPlugins } from "./plugins/runtime.js";
8
- import { loadTreeseedManifest } from "./tenant/config.js";
9
- const TREESEED_ENVIRONMENT_SCOPES = ["local", "staging", "prod"];
10
- const TREESEED_ENVIRONMENT_REQUIREMENTS = ["required", "conditional", "optional"];
11
- const TREESEED_ENVIRONMENT_TARGETS = [
12
- "local-file",
13
- "wrangler-dev-vars",
14
- "github-secret",
15
- "github-variable",
16
- "cloudflare-secret",
17
- "cloudflare-var",
18
- "config-file"
19
- ];
20
- const TREESEED_ENVIRONMENT_PURPOSES = ["dev", "save", "deploy", "destroy", "config"];
21
- const TREESEED_ENVIRONMENT_SENSITIVITY = ["secret", "plain", "derived"];
22
- const moduleDir = dirname(fileURLToPath(import.meta.url));
23
- const CORE_ENVIRONMENT_PATH = resolve(moduleDir, "env.yaml");
24
- const TENANT_ENVIRONMENT_OVERLAY_PATH = "src/env.yaml";
25
- function loadOptionalTenantConfig() {
26
- try {
27
- return loadTreeseedManifest();
28
- } catch {
29
- return void 0;
30
- }
31
- }
32
- function turnstileEnabled(context) {
33
- return context.deployConfig.turnstile?.enabled !== false;
34
- }
35
- function smtpEnabled(context) {
36
- return context.deployConfig.smtp?.enabled === true;
37
- }
38
- function generatedSecret(bytes = 24) {
39
- return randomBytes(bytes).toString("hex");
40
- }
41
- const VALUE_RESOLVERS = {
42
- generatedSecret: () => generatedSecret(),
43
- localFormsBypassDefault: () => "true"
44
- };
45
- const PREDICATES = {
46
- turnstileEnabled: (context) => turnstileEnabled(context),
47
- turnstileNonLocal: (context, scope) => turnstileEnabled(context) && scope !== "local",
48
- smtpEnabled: (context) => smtpEnabled(context),
49
- smtpNonLocal: (context, scope) => smtpEnabled(context) && scope !== "local"
50
- };
51
- function deepMerge(left, right) {
52
- if (Array.isArray(left) && Array.isArray(right)) {
53
- return [...right];
54
- }
55
- if (left && typeof left === "object" && !Array.isArray(left) && right && typeof right === "object" && !Array.isArray(right)) {
56
- const result = { ...left };
57
- for (const [key, value] of Object.entries(right)) {
58
- result[key] = key in result ? deepMerge(result[key], value) : value;
59
- }
60
- return result;
61
- }
62
- return right;
63
- }
64
- function normalizeOverlay(raw, label) {
65
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
66
- throw new Error(`Invalid Treeseed environment registry overlay from ${label}.`);
67
- }
68
- const overlay = raw;
69
- if (overlay.entries === void 0) {
70
- return { entries: {} };
71
- }
72
- if (!overlay.entries || typeof overlay.entries !== "object" || Array.isArray(overlay.entries)) {
73
- throw new Error(`Invalid Treeseed environment registry overlay entries in ${label}.`);
74
- }
75
- return overlay;
76
- }
77
- function readYamlOverlayIfPresent(filePath) {
78
- if (!existsSync(filePath)) {
79
- return null;
80
- }
81
- return normalizeOverlay(parseYaml(readFileSync(filePath, "utf8")), filePath);
82
- }
83
- function pluginEnvironmentCandidates(baseDir) {
84
- const dir = resolve(baseDir);
85
- return [
86
- resolve(dir, "env.yaml"),
87
- resolve(dir, "src/env.yaml"),
88
- resolve(dir, "../env.yaml"),
89
- resolve(dir, "../src/env.yaml"),
90
- resolve(dir, "../../env.yaml"),
91
- resolve(dir, "../../src/env.yaml")
92
- ];
93
- }
94
- function readPluginEnvironmentOverlay(baseDir) {
95
- for (const candidate of pluginEnvironmentCandidates(baseDir)) {
96
- const overlay = readYamlOverlayIfPresent(candidate);
97
- if (overlay) {
98
- return { path: candidate, overlay };
99
- }
100
- }
101
- return null;
102
- }
103
- function loadTreeseedEnvironmentOverlay(tenantRoot) {
104
- const overlayPath = resolve(tenantRoot, TENANT_ENVIRONMENT_OVERLAY_PATH);
105
- return {
106
- path: overlayPath,
107
- overlay: readYamlOverlayIfPresent(overlayPath) ?? { entries: {} }
108
- };
109
- }
110
- function resolveNamedValueResolver(ref) {
111
- if (!ref) return void 0;
112
- const resolver = VALUE_RESOLVERS[ref];
113
- if (!resolver) {
114
- throw new Error(`Unknown Treeseed environment value resolver "${ref}".`);
115
- }
116
- return resolver;
117
- }
118
- function resolveNamedPredicate(ref) {
119
- if (!ref) return void 0;
120
- const predicate = PREDICATES[ref];
121
- if (!predicate) {
122
- throw new Error(`Unknown Treeseed environment predicate "${ref}".`);
123
- }
124
- return predicate;
125
- }
126
- function materializeEntry(id, entry) {
127
- return {
128
- ...entry,
129
- id,
130
- defaultValue: resolveNamedValueResolver(entry.defaultValueRef),
131
- localDefaultValue: resolveNamedValueResolver(entry.localDefaultValueRef),
132
- isRelevant: resolveNamedPredicate(entry.relevanceRef),
133
- requiredWhen: resolveNamedPredicate(entry.requiredWhenRef)
134
- };
135
- }
136
- function mergeEntryYaml(baseEntry, id, override) {
137
- const merged = baseEntry ? deepMerge(baseEntry, override) : override;
138
- if (typeof merged.label !== "string" || typeof merged.group !== "string" || typeof merged.description !== "string" || typeof merged.howToGet !== "string" || !Array.isArray(merged.targets) || !Array.isArray(merged.scopes) || typeof merged.requirement !== "string" || !Array.isArray(merged.purposes) || typeof merged.sensitivity !== "string") {
139
- throw new Error(`Treeseed environment registry entry "${id}" is missing required metadata after merge.`);
140
- }
141
- return merged;
142
- }
143
- function collectOverlaySources(context) {
144
- const sources = [];
145
- const coreOverlay = readYamlOverlayIfPresent(CORE_ENVIRONMENT_PATH);
146
- if (!coreOverlay) {
147
- throw new Error(`Treeseed core environment registry file was not found at ${CORE_ENVIRONMENT_PATH}.`);
148
- }
149
- sources.push({ label: CORE_ENVIRONMENT_PATH, overlay: coreOverlay });
150
- for (const pluginEntry of context.plugins) {
151
- const fileOverlay = readPluginEnvironmentOverlay(pluginEntry.baseDir);
152
- if (fileOverlay) {
153
- sources.push({ label: fileOverlay.path, overlay: fileOverlay.overlay });
154
- }
155
- const overlaySource = pluginEntry.plugin.environmentRegistry;
156
- if (!overlaySource) {
157
- continue;
158
- }
159
- const pluginContext = {
160
- projectRoot: context.tenantRoot,
161
- tenantConfig: context.tenantConfig,
162
- deployConfig: context.deployConfig,
163
- pluginConfig: pluginEntry.config
164
- };
165
- const overlay = typeof overlaySource === "function" ? overlaySource(pluginContext) : overlaySource;
166
- if (overlay) {
167
- sources.push({
168
- label: `plugin ${pluginEntry.package}`,
169
- overlay: normalizeOverlay(overlay, `plugin ${pluginEntry.package}`)
170
- });
171
- }
172
- }
173
- const tenantOverlay = loadTreeseedEnvironmentOverlay(context.tenantRoot);
174
- sources.push({ label: tenantOverlay.path, overlay: tenantOverlay.overlay });
175
- return sources;
176
- }
177
- function resolveTreeseedEnvironmentContext(options = {}) {
178
- const deployConfig = options.deployConfig ?? loadTreeseedDeployConfig();
179
- const tenantConfig = options.tenantConfig ?? loadOptionalTenantConfig();
180
- const plugins = options.plugins ?? loadTreeseedPlugins(deployConfig);
181
- const tenantRoot = deployConfig.__tenantRoot ?? tenantConfig?.__tenantRoot ?? process.cwd();
182
- return {
183
- deployConfig,
184
- tenantConfig,
185
- plugins,
186
- tenantRoot
187
- };
188
- }
189
- function resolveTreeseedEnvironmentRegistry(options = {}) {
190
- const context = resolveTreeseedEnvironmentContext(options);
191
- const entriesById = /* @__PURE__ */ new Map();
192
- const order = [];
193
- for (const source of collectOverlaySources(context)) {
194
- for (const [id, override] of Object.entries(source.overlay.entries ?? {})) {
195
- const current = entriesById.get(id);
196
- entriesById.set(id, mergeEntryYaml(current, id, override ?? {}));
197
- if (!current) {
198
- order.push(id);
199
- }
200
- }
201
- }
202
- return {
203
- context,
204
- entries: order.map((id) => materializeEntry(id, entriesById.get(id)))
205
- };
206
- }
207
- function isTreeseedEnvironmentEntryRelevant(entry, context, scope, purpose) {
208
- if (!entry.scopes.includes(scope)) {
209
- return false;
210
- }
211
- if (purpose && !entry.purposes.includes(purpose)) {
212
- return false;
213
- }
214
- if (entry.isRelevant) {
215
- return entry.isRelevant(context, scope, purpose);
216
- }
217
- return true;
218
- }
219
- function isEntryRequired(entry, context, scope, purpose) {
220
- if (entry.requirement === "required") {
221
- return true;
222
- }
223
- if (entry.requirement === "conditional") {
224
- return entry.requiredWhen ? entry.requiredWhen(context, scope, purpose) : true;
225
- }
226
- return false;
227
- }
228
- function materializeDefaultValue(entry, context, scope) {
229
- const source = scope === "local" && entry.localDefaultValue !== void 0 ? entry.localDefaultValue : entry.defaultValue;
230
- if (source === void 0) {
231
- return void 0;
232
- }
233
- return typeof source === "function" ? source(context, scope) : source;
234
- }
235
- function getTreeseedEnvironmentSuggestedValues(options) {
236
- const registry = resolveTreeseedEnvironmentRegistry(options);
237
- return Object.fromEntries(
238
- registry.entries.filter((entry) => isTreeseedEnvironmentEntryRelevant(entry, registry.context, options.scope, options.purpose)).map((entry) => [entry.id, materializeDefaultValue(entry, registry.context, options.scope)]).filter(([, value]) => value !== void 0)
239
- );
240
- }
241
- function valuePresent(value) {
242
- return typeof value === "string" && value.trim().length > 0;
243
- }
244
- function validateValue(entry, value) {
245
- if (!entry.validation) {
246
- return null;
247
- }
248
- switch (entry.validation.kind) {
249
- case "string":
250
- case "nonempty":
251
- return valuePresent(value) ? null : `${entry.id} must be a non-empty string.`;
252
- case "boolean":
253
- return /^(true|false|1|0)$/i.test(value) ? null : `${entry.id} must be true or false.`;
254
- case "number":
255
- return Number.isFinite(Number(value)) ? null : `${entry.id} must be a number.`;
256
- case "url":
257
- try {
258
- new URL(value);
259
- return null;
260
- } catch {
261
- return `${entry.id} must be a valid URL.`;
262
- }
263
- case "email":
264
- return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) ? null : `${entry.id} must be a valid email address.`;
265
- case "enum":
266
- return entry.validation.values.includes(value) ? null : `${entry.id} must be one of: ${entry.validation.values.join(", ")}.`;
267
- default:
268
- return null;
269
- }
270
- }
271
- function validateTreeseedEnvironmentValues(options) {
272
- const registry = resolveTreeseedEnvironmentRegistry(options);
273
- const relevantEntries = registry.entries.filter(
274
- (entry) => isTreeseedEnvironmentEntryRelevant(entry, registry.context, options.scope, options.purpose)
275
- );
276
- const requiredEntries = relevantEntries.filter(
277
- (entry) => isEntryRequired(entry, registry.context, options.scope, options.purpose)
278
- );
279
- const missing = [];
280
- const invalid = [];
281
- for (const entry of requiredEntries) {
282
- const value = options.values[entry.id];
283
- if (!valuePresent(value)) {
284
- missing.push({
285
- id: entry.id,
286
- label: entry.label,
287
- reason: "missing",
288
- message: `${entry.id} is required for ${options.purpose} (${options.scope}). ${entry.howToGet}`,
289
- entry
290
- });
291
- continue;
292
- }
293
- const validationMessage = validateValue(entry, value);
294
- if (validationMessage) {
295
- invalid.push({
296
- id: entry.id,
297
- label: entry.label,
298
- reason: "invalid",
299
- message: validationMessage,
300
- entry
301
- });
302
- }
303
- }
304
- return {
305
- ok: missing.length === 0 && invalid.length === 0,
306
- entries: relevantEntries,
307
- required: requiredEntries,
308
- missing,
309
- invalid
310
- };
311
- }
1
+ import {
2
+ getTreeseedEnvironmentSuggestedValues,
3
+ isTreeseedEnvironmentEntryRelevant,
4
+ loadTreeseedEnvironmentOverlay,
5
+ resolveTreeseedEnvironmentContext,
6
+ resolveTreeseedEnvironmentRegistry,
7
+ TREESEED_ENVIRONMENT_PURPOSES,
8
+ TREESEED_ENVIRONMENT_REQUIREMENTS,
9
+ TREESEED_ENVIRONMENT_SCOPES,
10
+ TREESEED_ENVIRONMENT_SENSITIVITY,
11
+ TREESEED_ENVIRONMENT_TARGETS,
12
+ validateTreeseedEnvironmentValues
13
+ } from "@treeseed/sdk/platform/environment";
312
14
  export {
313
15
  TREESEED_ENVIRONMENT_PURPOSES,
314
16
  TREESEED_ENVIRONMENT_REQUIREMENTS,
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  getTreeseedDeployProvider,
13
13
  getTreeseedDocsProvider,
14
14
  getTreeseedFormsProvider,
15
+ getTreeseedOperationsProvider,
15
16
  getTreeseedSiteProvider,
16
17
  isTreeseedSmtpEnabled,
17
18
  isTreeseedTurnstileEnabled,
@@ -58,6 +59,7 @@ export {
58
59
  getTreeseedDocsProvider,
59
60
  getTreeseedEnvironmentSuggestedValues,
60
61
  getTreeseedFormsProvider,
62
+ getTreeseedOperationsProvider,
61
63
  getTreeseedSiteProvider,
62
64
  isTreeseedEnvironmentEntryRelevant,
63
65
  isTreeseedSmtpEnabled,
@@ -1,8 +1,10 @@
1
+ import { createDefaultGraphRankingProvider } from "@treeseed/sdk";
1
2
  import { defineTreeseedPlugin } from "../plugin.js";
2
3
  var default_plugin_default = defineTreeseedPlugin({
3
4
  id: "treeseed-core-default",
4
5
  provides: {
5
6
  forms: ["store_only", "notify_admin", "full_email"],
7
+ operations: ["default"],
6
8
  agents: {
7
9
  execution: ["stub", "manual", "copilot"],
8
10
  mutation: ["local_branch"],
@@ -25,6 +27,9 @@ var default_plugin_default = defineTreeseedPlugin({
25
27
  docs: ["default"]
26
28
  },
27
29
  site: ["default"]
30
+ },
31
+ graphRankingProviders: {
32
+ default: createDefaultGraphRankingProvider()
28
33
  }
29
34
  });
30
35
  export {
@@ -1,21 +1 @@
1
- export declare const TREESEED_DEFAULT_PLUGIN_PACKAGE = "@treeseed/core/plugin-default";
2
- export declare const TREESEED_DEFAULT_PROVIDER_SELECTIONS: {
3
- forms: string;
4
- agents: {
5
- execution: string;
6
- mutation: string;
7
- repository: string;
8
- verification: string;
9
- notification: string;
10
- research: string;
11
- };
12
- deploy: string;
13
- content: {
14
- docs: string;
15
- };
16
- site: string;
17
- };
18
- export declare const TREESEED_DEFAULT_PLUGIN_REFERENCES: {
19
- package: string;
20
- enabled: boolean;
21
- }[];
1
+ export { TREESEED_DEFAULT_PLUGIN_PACKAGE, TREESEED_DEFAULT_PLUGIN_REFERENCES, TREESEED_DEFAULT_PROVIDER_SELECTIONS, } from '@treeseed/sdk/platform/plugins';
@@ -1,26 +1,8 @@
1
- const TREESEED_DEFAULT_PLUGIN_PACKAGE = "@treeseed/core/plugin-default";
2
- const TREESEED_DEFAULT_PROVIDER_SELECTIONS = {
3
- forms: "store_only",
4
- agents: {
5
- execution: "stub",
6
- mutation: "local_branch",
7
- repository: "stub",
8
- verification: "stub",
9
- notification: "stub",
10
- research: "stub"
11
- },
12
- deploy: "cloudflare",
13
- content: {
14
- docs: "default"
15
- },
16
- site: "default"
17
- };
18
- const TREESEED_DEFAULT_PLUGIN_REFERENCES = [
19
- {
20
- package: TREESEED_DEFAULT_PLUGIN_PACKAGE,
21
- enabled: true
22
- }
23
- ];
1
+ import {
2
+ TREESEED_DEFAULT_PLUGIN_PACKAGE,
3
+ TREESEED_DEFAULT_PLUGIN_REFERENCES,
4
+ TREESEED_DEFAULT_PROVIDER_SELECTIONS
5
+ } from "@treeseed/sdk/platform/plugins";
24
6
  export {
25
7
  TREESEED_DEFAULT_PLUGIN_PACKAGE,
26
8
  TREESEED_DEFAULT_PLUGIN_REFERENCES,
@@ -1,42 +1,2 @@
1
- import type { TreeseedDeployConfig, TreeseedTenantConfig } from '../contracts';
2
- import type { TreeseedEnvironmentRegistryOverlay } from '../environment';
3
- import type { TreeseedSiteLayerDefinition } from '../site-resources';
4
- export type TreeseedSiteRouteContribution = {
5
- pattern: string;
6
- entrypoint?: string;
7
- resourcePath?: string;
8
- };
9
- export type TreeseedSiteExtensionContribution = {
10
- routes?: TreeseedSiteRouteContribution[];
11
- starlightComponents?: Record<string, string>;
12
- customCss?: string[];
13
- remarkPlugins?: unknown[];
14
- rehypePlugins?: unknown[];
15
- envSchema?: Record<string, unknown>;
16
- vitePlugins?: unknown[];
17
- integrations?: unknown[];
18
- routeMiddleware?: unknown[];
19
- };
20
- export type TreeseedPluginSiteContext = {
21
- projectRoot: string;
22
- tenantConfig: TreeseedTenantConfig;
23
- siteConfig?: unknown;
24
- deployConfig?: TreeseedDeployConfig;
25
- pluginConfig: Record<string, unknown>;
26
- };
27
- export type TreeseedPluginEnvironmentContext = {
28
- projectRoot: string;
29
- tenantConfig?: TreeseedTenantConfig;
30
- deployConfig?: TreeseedDeployConfig;
31
- pluginConfig: Record<string, unknown>;
32
- };
33
- export interface TreeseedPlugin {
34
- id?: string;
35
- provides?: Record<string, any>;
36
- siteProviders?: Record<string, TreeseedSiteExtensionContribution | ((context: TreeseedPluginSiteContext) => TreeseedSiteExtensionContribution)>;
37
- siteHooks?: TreeseedSiteExtensionContribution | ((context: TreeseedPluginSiteContext) => TreeseedSiteExtensionContribution);
38
- siteLayers?: TreeseedSiteLayerDefinition[] | ((context: TreeseedPluginSiteContext) => TreeseedSiteLayerDefinition[] | undefined);
39
- environmentRegistry?: TreeseedEnvironmentRegistryOverlay | ((context: TreeseedPluginEnvironmentContext) => TreeseedEnvironmentRegistryOverlay | undefined);
40
- [key: string]: unknown;
41
- }
42
- export declare function defineTreeseedPlugin<T extends TreeseedPlugin>(plugin: T): T;
1
+ export { defineTreeseedPlugin } from '@treeseed/sdk/platform/plugin';
2
+ export type * from '@treeseed/sdk/platform/plugin';
@@ -1,6 +1,4 @@
1
- function defineTreeseedPlugin(plugin) {
2
- return plugin;
3
- }
1
+ import { defineTreeseedPlugin } from "@treeseed/sdk/platform/plugin";
4
2
  export {
5
3
  defineTreeseedPlugin
6
4
  };
@@ -12,6 +12,7 @@ export declare function loadTreeseedPluginRuntime(config?: TreeseedDeployConfig)
12
12
  plugins: LoadedPluginEntry[];
13
13
  provided: {
14
14
  forms: Set<string>;
15
+ operations: Set<string>;
15
16
  agents: {
16
17
  execution: Set<string>;
17
18
  mutation: Set<string>;
@@ -1,10 +1,11 @@
1
+ import { existsSync } from "node:fs";
1
2
  import { createRequire } from "node:module";
2
3
  import path from "node:path";
3
4
  import { fileURLToPath } from "node:url";
4
5
  import { loadTreeseedDeployConfig } from "../deploy/config.js";
5
6
  import { TREESEED_DEFAULT_PLUGIN_PACKAGE } from "./constants.js";
6
- import builtinDefaultPlugin from "./builtin/default-plugin.js";
7
7
  const require2 = createRequire(import.meta.url);
8
+ const runtimeDir = path.dirname(fileURLToPath(import.meta.url));
8
9
  function normalizeLoadedPlugin(moduleExports, packageName) {
9
10
  const plugin = moduleExports?.default ?? moduleExports;
10
11
  if (!plugin || typeof plugin !== "object") {
@@ -15,11 +16,25 @@ function normalizeLoadedPlugin(moduleExports, packageName) {
15
16
  function isPathLikePluginReference(packageName) {
16
17
  return packageName.startsWith(".") || packageName.startsWith("/") || packageName.startsWith("file:");
17
18
  }
19
+ function resolveLocalDefaultPluginPath() {
20
+ const candidates = [
21
+ path.resolve(runtimeDir, "../plugin-default.js"),
22
+ path.resolve(runtimeDir, "../../dist/plugin-default.js")
23
+ ];
24
+ for (const candidate of candidates) {
25
+ if (existsSync(candidate)) {
26
+ return candidate;
27
+ }
28
+ }
29
+ return null;
30
+ }
18
31
  function loadPluginModule(packageName, tenantRoot) {
19
32
  if (packageName === TREESEED_DEFAULT_PLUGIN_PACKAGE) {
33
+ const localDefaultPluginPath = resolveLocalDefaultPluginPath();
34
+ const resolvedPath2 = localDefaultPluginPath ?? require2.resolve(packageName);
20
35
  return {
21
- moduleExports: builtinDefaultPlugin,
22
- baseDir: fileURLToPath(new URL("..", import.meta.url))
36
+ moduleExports: require2(resolvedPath2),
37
+ baseDir: path.dirname(resolvedPath2)
23
38
  };
24
39
  }
25
40
  if (isPathLikePluginReference(packageName)) {
@@ -56,6 +71,7 @@ function loadTreeseedPlugins(config = loadTreeseedDeployConfig()) {
56
71
  function collectProvidedIds(plugins) {
57
72
  const provided = {
58
73
  forms: /* @__PURE__ */ new Set(),
74
+ operations: /* @__PURE__ */ new Set(),
59
75
  agents: {
60
76
  execution: /* @__PURE__ */ new Set(),
61
77
  mutation: /* @__PURE__ */ new Set(),
@@ -73,6 +89,7 @@ function collectProvidedIds(plugins) {
73
89
  };
74
90
  for (const { plugin } of plugins) {
75
91
  for (const id of plugin.provides?.forms ?? []) provided.forms.add(id);
92
+ for (const id of plugin.provides?.operations ?? []) provided.operations.add(id);
76
93
  for (const id of plugin.provides?.agents?.execution ?? []) provided.agents.execution.add(id);
77
94
  for (const id of plugin.provides?.agents?.mutation ?? []) provided.agents.mutation.add(id);
78
95
  for (const id of plugin.provides?.agents?.repository ?? []) provided.agents.repository.add(id);
@@ -99,6 +116,7 @@ function loadTreeseedPluginRuntime(config = loadTreeseedDeployConfig()) {
99
116
  const provided = collectProvidedIds(plugins);
100
117
  const providers = config.providers;
101
118
  assertSelectedProvider(provided.forms, "forms", providers.forms);
119
+ assertSelectedProvider(provided.operations, "operations", providers.operations);
102
120
  assertSelectedProvider(provided.agents.execution, "agents.execution", providers.agents.execution);
103
121
  assertSelectedProvider(provided.agents.mutation, "agents.mutation", providers.agents.mutation);
104
122
  assertSelectedProvider(provided.agents.repository, "agents.repository", providers.agents.repository);