@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.
- package/README.md +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/content-config.d.ts +10 -0
- package/dist/content.js +44 -10
- package/dist/contracts.d.ts +1 -130
- package/dist/deploy/config.d.ts +1 -4
- package/dist/deploy/config.js +4 -148
- package/dist/deploy/runtime.js +4 -0
- package/dist/env.yaml +86 -14
- package/dist/environment.d.ts +2 -130
- package/dist/environment.js +13 -311
- package/dist/index.js +2 -0
- package/dist/plugins/builtin/default-plugin.js +5 -0
- package/dist/plugins/constants.d.ts +1 -21
- package/dist/plugins/constants.js +5 -23
- package/dist/plugins/plugin.d.ts +2 -42
- package/dist/plugins/plugin.js +1 -3
- package/dist/plugins/runtime.d.ts +1 -0
- package/dist/plugins/runtime.js +21 -3
- package/dist/scripts/build-dist.js +5 -1
- package/dist/scripts/release-verify.js +1 -2
- package/dist/scripts/test-smoke.js +14 -3
- package/dist/scripts/workspace-bootstrap.js +153 -0
- package/dist/tenant/config.js +8 -116
- package/dist/utils/books-data.js +7 -75
- package/dist/utils/site-config-schema.js +1 -282
- package/package.json +10 -5
- package/templates/github/deploy.workflow.yml +1 -1
- package/dist/plugins/builtin/default-plugin.d.ts +0 -21
- package/dist/tenant/config.d.ts +0 -9
package/dist/environment.d.ts
CHANGED
|
@@ -1,130 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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';
|
package/dist/environment.js
CHANGED
|
@@ -1,314 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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,
|
package/dist/plugins/plugin.d.ts
CHANGED
|
@@ -1,42 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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';
|
package/dist/plugins/plugin.js
CHANGED
package/dist/plugins/runtime.js
CHANGED
|
@@ -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:
|
|
22
|
-
baseDir:
|
|
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);
|