@treeseed/sdk 0.1.2 → 0.3.1
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 +97 -506
- package/dist/{src/cli-tools.d.ts → cli-tools.d.ts} +1 -1
- package/dist/cli-tools.js +5 -3
- package/dist/{src/content-store.d.ts → content-store.d.ts} +3 -2
- package/dist/content-store.js +52 -20
- package/dist/{src/d1-store.d.ts → d1-store.d.ts} +62 -1
- package/dist/d1-store.js +625 -65
- package/dist/field-aliases.d.ts +11 -0
- package/dist/field-aliases.js +41 -0
- package/dist/graph/build.d.ts +19 -0
- package/dist/graph/build.js +949 -0
- package/dist/graph/dsl.d.ts +2 -0
- package/dist/graph/dsl.js +243 -0
- package/dist/graph/query.d.ts +47 -0
- package/dist/graph/query.js +447 -0
- package/dist/graph/ranking.d.ts +3 -0
- package/dist/graph/ranking.js +483 -0
- package/dist/graph/schema.d.ts +142 -0
- package/dist/graph/schema.js +133 -0
- package/dist/graph.d.ts +52 -0
- package/dist/graph.js +133 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +91 -2
- package/dist/model-registry.d.ts +8 -0
- package/dist/model-registry.js +351 -25
- package/dist/operations/providers/default.d.ts +10 -0
- package/dist/operations/providers/default.js +514 -0
- package/dist/operations/runtime.d.ts +7 -0
- package/dist/operations/runtime.js +60 -0
- package/dist/operations/services/config-runtime.d.ts +269 -0
- package/dist/operations/services/config-runtime.js +1397 -0
- package/dist/operations/services/d1-migration.d.ts +6 -0
- package/dist/operations/services/d1-migration.js +89 -0
- package/dist/operations/services/deploy.d.ts +371 -0
- package/dist/operations/services/deploy.js +981 -0
- package/dist/operations/services/git-workflow.d.ts +49 -0
- package/dist/operations/services/git-workflow.js +218 -0
- package/dist/operations/services/github-automation.d.ts +156 -0
- package/dist/operations/services/github-automation.js +256 -0
- package/dist/operations/services/local-dev.d.ts +9 -0
- package/dist/operations/services/local-dev.js +106 -0
- package/dist/operations/services/mailpit-runtime.d.ts +4 -0
- package/dist/operations/services/mailpit-runtime.js +59 -0
- package/dist/operations/services/railway-deploy.d.ts +53 -0
- package/dist/operations/services/railway-deploy.js +123 -0
- package/dist/operations/services/runtime-paths.d.ts +19 -0
- package/dist/operations/services/runtime-paths.js +54 -0
- package/dist/operations/services/runtime-tools.d.ts +117 -0
- package/dist/operations/services/runtime-tools.js +358 -0
- package/dist/operations/services/save-deploy-preflight.d.ts +34 -0
- package/dist/operations/services/save-deploy-preflight.js +76 -0
- package/dist/operations/services/template-registry.d.ts +88 -0
- package/dist/operations/services/template-registry.js +407 -0
- package/dist/operations/services/watch-dev.d.ts +21 -0
- package/dist/operations/services/watch-dev.js +284 -0
- package/dist/operations/services/workspace-preflight.d.ts +40 -0
- package/dist/operations/services/workspace-preflight.js +165 -0
- package/dist/operations/services/workspace-save.d.ts +42 -0
- package/dist/operations/services/workspace-save.js +235 -0
- package/dist/operations/services/workspace-tools.d.ts +16 -0
- package/dist/operations/services/workspace-tools.js +270 -0
- package/dist/operations-registry.d.ts +5 -0
- package/dist/operations-registry.js +68 -0
- package/dist/operations-types.d.ts +71 -0
- package/dist/operations-types.js +17 -0
- package/dist/operations.d.ts +6 -0
- package/dist/operations.js +16 -0
- package/dist/platform/books-data.d.ts +1 -0
- package/dist/platform/books-data.js +1 -0
- package/dist/platform/contracts.d.ts +158 -0
- package/dist/platform/contracts.js +0 -0
- package/dist/platform/deploy/config.d.ts +4 -0
- package/dist/platform/deploy/config.js +222 -0
- package/dist/platform/deploy-config.d.ts +1 -0
- package/dist/platform/deploy-config.js +1 -0
- package/dist/platform/deploy-runtime.d.ts +18 -0
- package/dist/platform/deploy-runtime.js +78 -0
- package/dist/platform/env.yaml +394 -0
- package/dist/platform/environment.d.ts +130 -0
- package/dist/platform/environment.js +331 -0
- package/dist/platform/plugin.d.ts +2 -0
- package/dist/platform/plugin.js +4 -0
- package/dist/platform/plugins/constants.d.ts +22 -0
- package/dist/platform/plugins/constants.js +29 -0
- package/dist/platform/plugins/plugin.d.ts +51 -0
- package/dist/platform/plugins/plugin.js +6 -0
- package/dist/platform/plugins/runtime.d.ts +35 -0
- package/dist/platform/plugins/runtime.js +161 -0
- package/dist/platform/plugins.d.ts +6 -0
- package/dist/platform/plugins.js +38 -0
- package/dist/platform/site-config-schema.js +1 -0
- package/dist/platform/tenant/config.d.ts +9 -0
- package/dist/platform/tenant/config.js +154 -0
- package/dist/platform/tenant/runtime-config.d.ts +4 -0
- package/dist/platform/tenant/runtime-config.js +20 -0
- package/dist/platform/tenant-config.d.ts +1 -0
- package/dist/platform/tenant-config.js +1 -0
- package/dist/platform/utils/books-data.d.ts +29 -0
- package/dist/platform/utils/books-data.js +82 -0
- package/dist/platform/utils/site-config-schema.js +321 -0
- package/dist/remote.d.ts +175 -0
- package/dist/remote.js +202 -0
- package/dist/runtime.js +35 -22
- package/dist/scripts/aggregate-book.js +121 -0
- package/dist/scripts/build-dist.js +54 -13
- package/dist/scripts/build-tenant-worker.js +36 -0
- package/dist/scripts/cleanup-markdown.js +373 -0
- package/dist/scripts/cli-test-fixtures.js +48 -0
- package/dist/scripts/config-treeseed.js +95 -0
- package/dist/scripts/ensure-mailpit.js +29 -0
- package/dist/scripts/local-dev.js +129 -0
- package/dist/scripts/logs-mailpit.js +2 -0
- package/dist/scripts/patch-starlight-content-path.js +172 -0
- package/dist/scripts/release-verify.js +34 -6
- package/dist/scripts/run-fixture-astro-command.js +18 -0
- package/dist/scripts/scaffold-site.js +65 -0
- package/dist/scripts/stop-mailpit.js +5 -0
- package/dist/scripts/sync-dev-vars.js +6 -0
- package/dist/scripts/sync-template.js +20 -0
- package/dist/scripts/template-catalog.test.js +100 -0
- package/dist/scripts/template-command.js +31 -0
- package/dist/scripts/tenant-astro-command.js +3 -0
- package/dist/scripts/tenant-build.js +16 -0
- package/dist/scripts/tenant-check.js +7 -0
- package/dist/scripts/tenant-d1-migrate-local.js +11 -0
- package/dist/scripts/tenant-deploy.js +180 -0
- package/dist/scripts/tenant-destroy.js +104 -0
- package/dist/scripts/tenant-dev.js +171 -0
- package/dist/scripts/tenant-lint.js +4 -0
- package/dist/scripts/tenant-test.js +4 -0
- package/dist/scripts/test-cloudflare-local.js +212 -0
- package/dist/scripts/test-scaffold.js +314 -0
- package/dist/scripts/test-smoke.js +71 -13
- package/dist/scripts/treeseed-assert-release-tag-version.js +21 -0
- package/dist/scripts/treeseed-build-dist.js +134 -0
- package/dist/scripts/treeseed-publish-package.js +19 -0
- package/dist/scripts/treeseed-release-verify.js +131 -0
- package/dist/scripts/treeseed-run-ts.js +45 -0
- package/dist/scripts/validate-templates.js +6 -0
- package/dist/scripts/verify-driver.js +29 -0
- package/dist/scripts/workflow-commands.test.js +39 -0
- package/dist/scripts/workspace-close.js +24 -0
- package/dist/scripts/workspace-command-e2e.js +718 -0
- package/dist/scripts/workspace-lint.js +9 -0
- package/dist/scripts/workspace-preflight.js +22 -0
- package/dist/scripts/workspace-publish-changed-packages.js +16 -0
- package/dist/scripts/workspace-release-verify.js +81 -0
- package/dist/scripts/workspace-release.js +42 -0
- package/dist/scripts/workspace-save.js +124 -0
- package/dist/scripts/workspace-start-warning.js +3 -0
- package/dist/scripts/workspace-start.js +71 -0
- package/dist/scripts/workspace-test-unit.js +4 -0
- package/dist/scripts/workspace-test.js +11 -0
- package/dist/sdk-fields.d.ts +11 -0
- package/dist/sdk-fields.js +169 -0
- package/dist/sdk-filters.d.ts +4 -0
- package/dist/sdk-filters.js +12 -15
- package/dist/sdk-types.d.ts +796 -0
- package/dist/sdk-types.js +7 -1
- package/dist/sdk-version.d.ts +2 -0
- package/dist/sdk-version.js +42 -0
- package/dist/sdk.d.ts +215 -0
- package/dist/sdk.js +235 -11
- package/dist/stores/cursor-store.js +9 -3
- package/dist/stores/lease-store.js +8 -2
- package/dist/{src/stores → stores}/message-store.d.ts +1 -1
- package/dist/stores/message-store.js +27 -3
- package/dist/stores/operational-store.d.ts +24 -0
- package/dist/stores/operational-store.js +279 -0
- package/dist/stores/run-store.js +8 -1
- package/dist/stores/subscription-store.js +7 -5
- package/dist/template-catalog.d.ts +13 -0
- package/dist/template-catalog.js +141 -0
- package/dist/treeseed/services/compose.yml +7 -0
- package/dist/treeseed/template-catalog/catalog.fixture.json +55 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/astro.config.d.ts +2 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/astro.config.ts +3 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/package.json +32 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/src/config.yaml +40 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/empty/.gitkeep +1 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/knowledge/handbook/index.mdx +11 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/pages/welcome.mdx +11 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content.config.d.ts +1 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content.config.ts +3 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/src/env.yaml +1 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/src/manifest.yaml +19 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/treeseed.site.yaml +26 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template/tsconfig.json +9 -0
- package/dist/treeseed/template-catalog/templates/starter-basic/template.config.json +90 -0
- package/dist/utils/agents/contracts/messages.d.ts +88 -0
- package/dist/utils/agents/contracts/messages.js +138 -0
- package/dist/utils/agents/contracts/run.d.ts +20 -0
- package/dist/utils/agents/contracts/run.js +0 -0
- package/dist/utils/agents/runtime-types.d.ts +117 -0
- package/dist/utils/agents/runtime-types.js +4 -0
- package/dist/verification.d.ts +20 -0
- package/dist/verification.js +98 -0
- package/dist/workflow/operations.d.ts +396 -0
- package/dist/workflow/operations.js +841 -0
- package/dist/workflow-state.d.ts +56 -0
- package/dist/workflow-state.js +195 -0
- package/dist/workflow-support.d.ts +9 -0
- package/dist/workflow-support.js +176 -0
- package/dist/workflow.d.ts +111 -0
- package/dist/workflow.js +97 -0
- package/package.json +111 -5
- package/scripts/verify-driver.mjs +29 -0
- package/dist/scripts/.ts-run-1775630384291-crtqr3izsa.js +0 -22
- package/dist/scripts/.ts-run-1775630388025-vnjle0z75a.js +0 -129
- package/dist/scripts/assert-release-tag-version.d.ts +0 -1
- package/dist/scripts/build-dist.d.ts +0 -1
- package/dist/scripts/fixture-tools.d.ts +0 -5
- package/dist/scripts/package-tools.d.ts +0 -15
- package/dist/scripts/publish-package.d.ts +0 -1
- package/dist/scripts/release-verify.d.ts +0 -1
- package/dist/scripts/test-smoke.d.ts +0 -1
- package/dist/src/index.d.ts +0 -6
- package/dist/src/model-registry.d.ts +0 -4
- package/dist/src/sdk-filters.d.ts +0 -4
- package/dist/src/sdk-types.d.ts +0 -285
- package/dist/src/sdk.d.ts +0 -109
- package/dist/test/test-fixture.d.ts +0 -1
- package/dist/test/utils/envelopes.test.d.ts +0 -1
- package/dist/test/utils/sdk.test.d.ts +0 -1
- package/dist/vitest.config.d.ts +0 -2
- /package/dist/{src/frontmatter.d.ts → frontmatter.d.ts} +0 -0
- /package/dist/{src/git-runtime.d.ts → git-runtime.d.ts} +0 -0
- /package/dist/{src/runtime.d.ts → runtime.d.ts} +0 -0
- /package/dist/{src/stores → stores}/cursor-store.d.ts +0 -0
- /package/dist/{src/stores → stores}/envelopes.d.ts +0 -0
- /package/dist/{src/stores → stores}/helpers.d.ts +0 -0
- /package/dist/{src/stores → stores}/lease-store.d.ts +0 -0
- /package/dist/{src/stores → stores}/run-store.d.ts +0 -0
- /package/dist/{src/stores → stores}/subscription-store.d.ts +0 -0
- /package/dist/{src/types → types}/agents.d.ts +0 -0
- /package/dist/{src/types → types}/cloudflare.d.ts +0 -0
- /package/dist/{src/wrangler-d1.d.ts → wrangler-d1.d.ts} +0 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { packageScriptPath } from '../operations/services/runtime-tools.js';
|
|
2
|
+
import { packagesWithScript, run, workspaceRoot } from '../operations/services/workspace-tools.js';
|
|
3
|
+
const root = workspaceRoot();
|
|
4
|
+
run(process.execPath, [packageScriptPath('cleanup-markdown'), '--check'], {
|
|
5
|
+
cwd: root,
|
|
6
|
+
});
|
|
7
|
+
for (const pkg of packagesWithScript('lint', root)) {
|
|
8
|
+
run('npm', ['run', 'lint'], { cwd: pkg.dir });
|
|
9
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { collectCliPreflight, formatCliPreflightReport, writeJsonArtifact } from '../operations/services/workspace-preflight.js';
|
|
4
|
+
const args = new Set(process.argv.slice(2));
|
|
5
|
+
const requireAuth = args.has('--require-auth');
|
|
6
|
+
const json = args.has('--json');
|
|
7
|
+
const reportPathArgIndex = process.argv.indexOf('--report-path');
|
|
8
|
+
const reportPath = reportPathArgIndex >= 0 ? process.argv[reportPathArgIndex + 1] : process.env.TREESEED_PREFLIGHT_REPORT_PATH;
|
|
9
|
+
const report = collectCliPreflight({
|
|
10
|
+
cwd: process.cwd(),
|
|
11
|
+
requireAuth,
|
|
12
|
+
});
|
|
13
|
+
if (reportPath) {
|
|
14
|
+
writeJsonArtifact(resolve(reportPath), report);
|
|
15
|
+
}
|
|
16
|
+
if (json) {
|
|
17
|
+
console.log(JSON.stringify(report, null, 2));
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
console.log(formatCliPreflightReport(report));
|
|
21
|
+
}
|
|
22
|
+
process.exit(report.ok ? 0 : 1);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { packageScriptPath } from '../operations/services/runtime-tools.js';
|
|
2
|
+
import { publishableWorkspacePackages, changedWorkspacePackages, run } from '../operations/services/workspace-tools.js';
|
|
3
|
+
const publishablePackages = publishableWorkspacePackages();
|
|
4
|
+
const changed = changedWorkspacePackages({ packages: publishablePackages, includeDependents: true });
|
|
5
|
+
if (changed.length === 0) {
|
|
6
|
+
console.log('No changed workspace packages to publish.');
|
|
7
|
+
process.exit(0);
|
|
8
|
+
}
|
|
9
|
+
console.log(`Publishing changed workspace packages in order: ${changed.map((pkg) => pkg.name).join(', ')}`);
|
|
10
|
+
run(process.execPath, [packageScriptPath('workspace-release-verify'), '--changed', '--full-smoke']);
|
|
11
|
+
for (const pkg of changed) {
|
|
12
|
+
console.log(`Publishing ${pkg.name}`);
|
|
13
|
+
run('npm', ['run', 'release:publish'], {
|
|
14
|
+
cwd: pkg.dir,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { publishableWorkspacePackages, changedWorkspacePackages, } from '../operations/services/workspace-tools.js';
|
|
3
|
+
const cliArgs = new Set(process.argv.slice(2));
|
|
4
|
+
const verifyChangedOnly = cliArgs.has('--changed');
|
|
5
|
+
const fullSmoke = cliArgs.has('--full-smoke') || process.env.TREESEED_RELEASE_FULL_SMOKE === '1';
|
|
6
|
+
const publishablePackages = publishableWorkspacePackages();
|
|
7
|
+
const packagesToVerify = verifyChangedOnly
|
|
8
|
+
? changedWorkspacePackages({ packages: publishablePackages, includeDependents: true })
|
|
9
|
+
: publishablePackages;
|
|
10
|
+
const timings = [];
|
|
11
|
+
function nowLabel() {
|
|
12
|
+
return new Date().toISOString();
|
|
13
|
+
}
|
|
14
|
+
function logStep(message) {
|
|
15
|
+
console.log(`[release-verify ${nowLabel()}] ${message}`);
|
|
16
|
+
}
|
|
17
|
+
async function withTiming(label, action) {
|
|
18
|
+
const startedAt = Date.now();
|
|
19
|
+
logStep(`${label} started`);
|
|
20
|
+
try {
|
|
21
|
+
await action();
|
|
22
|
+
const durationMs = Date.now() - startedAt;
|
|
23
|
+
timings.push({ label, durationMs, status: 'completed' });
|
|
24
|
+
logStep(`${label} completed in ${(durationMs / 1000).toFixed(1)}s`);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
const durationMs = Date.now() - startedAt;
|
|
28
|
+
timings.push({ label, durationMs, status: 'failed' });
|
|
29
|
+
logStep(`${label} failed in ${(durationMs / 1000).toFixed(1)}s`);
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function printSummary() {
|
|
34
|
+
if (timings.length === 0) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
console.log('[release-verify] Stage summary');
|
|
38
|
+
for (const entry of timings) {
|
|
39
|
+
console.log(`[release-verify] ${entry.status === 'completed' ? 'ok ' : 'fail'} ${entry.label} (${(entry.durationMs / 1000).toFixed(1)}s)`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function verifyManifest(pkg) {
|
|
43
|
+
for (const [dep, value] of Object.entries(pkg.packageJson.dependencies ?? {})) {
|
|
44
|
+
if (!dep.startsWith('@treeseed/')) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (String(value).startsWith('file:') || String(value).startsWith('workspace:')) {
|
|
48
|
+
throw new Error(`${pkg.name} dependency ${dep} must not use local-only specifier "${value}".`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function runPackageVerify(pkg) {
|
|
53
|
+
const extraArgs = fullSmoke ? ['--', '--full-smoke'] : [];
|
|
54
|
+
const result = spawnSync('npm', ['run', 'verify', ...extraArgs], {
|
|
55
|
+
cwd: pkg.dir,
|
|
56
|
+
stdio: 'inherit',
|
|
57
|
+
env: process.env,
|
|
58
|
+
});
|
|
59
|
+
if (result.status !== 0) {
|
|
60
|
+
throw new Error(`${pkg.name} verify failed`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (packagesToVerify.length === 0) {
|
|
64
|
+
console.log('No changed workspace packages to verify.');
|
|
65
|
+
process.exit(0);
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
logStep(`verifying ${packagesToVerify.map((pkg) => pkg.name).join(', ')} with package-owned ${fullSmoke ? 'full' : 'fast'} verification`);
|
|
69
|
+
for (const pkg of packagesToVerify) {
|
|
70
|
+
await withTiming(`${pkg.name} manifest verification`, async () => {
|
|
71
|
+
verifyManifest(pkg);
|
|
72
|
+
});
|
|
73
|
+
await withTiming(`${pkg.name} verify`, async () => {
|
|
74
|
+
runPackageVerify(pkg);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
console.log(`Release verification passed for: ${packagesToVerify.map((pkg) => pkg.name).join(', ')}`);
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
printSummary();
|
|
81
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { applyTreeseedEnvironmentToProcess } from '../operations/services/config-runtime.js';
|
|
5
|
+
import { PRODUCTION_BRANCH, STAGING_BRANCH, mergeStagingIntoMain, prepareReleaseBranches, pushBranch } from '../operations/services/git-workflow.js';
|
|
6
|
+
import { incrementVersion, planWorkspaceReleaseBump, applyWorkspaceVersionChanges, repoRoot } from '../operations/services/workspace-save.js';
|
|
7
|
+
import { run, workspaceRoot } from '../operations/services/workspace-tools.js';
|
|
8
|
+
import { runWorkspaceSavePreflight } from '../operations/services/save-deploy-preflight.js';
|
|
9
|
+
function parseArgs(argv) {
|
|
10
|
+
const flags = new Set(argv);
|
|
11
|
+
const selected = ['major', 'minor', 'patch'].filter((level) => flags.has(`--${level}`));
|
|
12
|
+
if (selected.length !== 1) {
|
|
13
|
+
throw new Error('Treeseed release requires exactly one version bump flag: --major, --minor, or --patch.');
|
|
14
|
+
}
|
|
15
|
+
return { level: selected[0] };
|
|
16
|
+
}
|
|
17
|
+
function bumpRootPackageJson(root, level) {
|
|
18
|
+
const packageJsonPath = resolve(root, 'package.json');
|
|
19
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
20
|
+
packageJson.version = incrementVersion(packageJson.version, level);
|
|
21
|
+
writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf8');
|
|
22
|
+
return packageJson.version;
|
|
23
|
+
}
|
|
24
|
+
const { level } = parseArgs(process.argv.slice(2));
|
|
25
|
+
const root = workspaceRoot();
|
|
26
|
+
const gitRoot = repoRoot(root);
|
|
27
|
+
prepareReleaseBranches(root);
|
|
28
|
+
applyTreeseedEnvironmentToProcess({ tenantRoot: root, scope: 'staging', override: true });
|
|
29
|
+
runWorkspaceSavePreflight({ cwd: root });
|
|
30
|
+
const plan = planWorkspaceReleaseBump(level, root);
|
|
31
|
+
applyWorkspaceVersionChanges(plan);
|
|
32
|
+
const rootVersion = bumpRootPackageJson(root, level);
|
|
33
|
+
run('git', ['checkout', STAGING_BRANCH], { cwd: gitRoot });
|
|
34
|
+
run('git', ['add', '-A'], { cwd: gitRoot });
|
|
35
|
+
run('git', ['commit', '-m', `release: ${level} bump`], { cwd: gitRoot });
|
|
36
|
+
pushBranch(gitRoot, STAGING_BRANCH);
|
|
37
|
+
mergeStagingIntoMain(root);
|
|
38
|
+
console.log('Treeseed release completed successfully.');
|
|
39
|
+
console.log(`Staging branch: ${STAGING_BRANCH}`);
|
|
40
|
+
console.log(`Production branch: ${PRODUCTION_BRANCH}`);
|
|
41
|
+
console.log(`Release level: ${level}`);
|
|
42
|
+
console.log(`Root version: ${rootVersion}`);
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { writeFileSync, mkdirSync } from 'node:fs';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
import { applyTreeseedEnvironmentToProcess } from '../operations/services/config-runtime.js';
|
|
5
|
+
import { collectMergeConflictReport, currentBranch, formatMergeConflictReport, hasMeaningfulChanges, originRemoteUrl, repoRoot, } from '../operations/services/workspace-save.js';
|
|
6
|
+
import { remoteBranchExists, STAGING_BRANCH, PRODUCTION_BRANCH } from '../operations/services/git-workflow.js';
|
|
7
|
+
import { run, workspaceRoot } from '../operations/services/workspace-tools.js';
|
|
8
|
+
import { runWorkspaceSavePreflight } from '../operations/services/save-deploy-preflight.js';
|
|
9
|
+
function writeSaveReport(payload) {
|
|
10
|
+
const target = process.env.TREESEED_SAVE_REPORT_PATH;
|
|
11
|
+
if (!target) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const filePath = resolve(target);
|
|
15
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
16
|
+
writeFileSync(filePath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
|
|
17
|
+
}
|
|
18
|
+
function parseArgs(argv) {
|
|
19
|
+
const parsed = {
|
|
20
|
+
hotfix: false,
|
|
21
|
+
messageParts: [],
|
|
22
|
+
};
|
|
23
|
+
for (const current of argv) {
|
|
24
|
+
if (current === '--hotfix') {
|
|
25
|
+
parsed.hotfix = true;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
parsed.messageParts.push(current);
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
hotfix: parsed.hotfix,
|
|
32
|
+
message: parsed.messageParts.join(' ').trim(),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const options = parseArgs(process.argv.slice(2));
|
|
36
|
+
const message = options.message;
|
|
37
|
+
const root = workspaceRoot();
|
|
38
|
+
const gitRoot = repoRoot(root);
|
|
39
|
+
const branch = currentBranch(gitRoot);
|
|
40
|
+
const scope = branch === STAGING_BRANCH ? 'staging' : branch === PRODUCTION_BRANCH ? 'prod' : 'local';
|
|
41
|
+
applyTreeseedEnvironmentToProcess({ tenantRoot: root, scope, override: true });
|
|
42
|
+
if (!message) {
|
|
43
|
+
writeSaveReport({ ok: false, kind: 'usage', message: 'Treeseed save requires a commit message.' });
|
|
44
|
+
console.error('Treeseed save requires a commit message. Usage: treeseed save <message>');
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
if (!branch) {
|
|
48
|
+
writeSaveReport({ ok: false, kind: 'missing_branch', message: 'Treeseed save requires an active git branch.' });
|
|
49
|
+
console.error('Treeseed save requires an active git branch.');
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
if (branch === PRODUCTION_BRANCH && !options.hotfix) {
|
|
53
|
+
writeSaveReport({
|
|
54
|
+
ok: false,
|
|
55
|
+
kind: 'protected_branch',
|
|
56
|
+
branch,
|
|
57
|
+
message: 'Treeseed save is blocked on main. Use `treeseed release` for normal production promotion or `treeseed save --hotfix` for an explicit hotfix.',
|
|
58
|
+
});
|
|
59
|
+
console.error('Treeseed save is blocked on main. Use `treeseed release` for normal production promotion or `treeseed save --hotfix` for an explicit hotfix.');
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
originRemoteUrl(gitRoot);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
writeSaveReport({ ok: false, kind: 'missing_origin', message: 'Treeseed save requires an origin remote.' });
|
|
67
|
+
console.error('Treeseed save requires an origin remote.');
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
runWorkspaceSavePreflight({ cwd: root });
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
const kind = error?.kind ?? 'preflight_failed';
|
|
75
|
+
writeSaveReport({
|
|
76
|
+
ok: false,
|
|
77
|
+
kind,
|
|
78
|
+
message: error instanceof Error ? error.message : String(error),
|
|
79
|
+
});
|
|
80
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
81
|
+
process.exit(error?.exitCode ?? 1);
|
|
82
|
+
}
|
|
83
|
+
if (!hasMeaningfulChanges(gitRoot)) {
|
|
84
|
+
writeSaveReport({ ok: false, kind: 'no_changes', message: 'Treeseed save found no meaningful repository changes to commit.' });
|
|
85
|
+
console.error('Treeseed save found no meaningful repository changes to commit.');
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
run('git', ['add', '-A'], { cwd: gitRoot });
|
|
89
|
+
run('git', ['commit', '-m', message], { cwd: gitRoot });
|
|
90
|
+
try {
|
|
91
|
+
if (remoteBranchExists(gitRoot, branch)) {
|
|
92
|
+
run('git', ['pull', '--rebase', 'origin', branch], { cwd: gitRoot });
|
|
93
|
+
run('git', ['push', 'origin', branch], { cwd: gitRoot });
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
run('git', ['push', '-u', 'origin', branch], { cwd: gitRoot });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
const report = collectMergeConflictReport(gitRoot);
|
|
101
|
+
writeSaveReport({
|
|
102
|
+
ok: false,
|
|
103
|
+
kind: 'merge_conflict',
|
|
104
|
+
branch,
|
|
105
|
+
report,
|
|
106
|
+
formatted: formatMergeConflictReport(report, gitRoot, branch),
|
|
107
|
+
});
|
|
108
|
+
console.error(formatMergeConflictReport(report, gitRoot, branch));
|
|
109
|
+
process.exit(12);
|
|
110
|
+
}
|
|
111
|
+
const summary = {
|
|
112
|
+
ok: true,
|
|
113
|
+
kind: 'success',
|
|
114
|
+
message,
|
|
115
|
+
branch,
|
|
116
|
+
scope,
|
|
117
|
+
hotfix: options.hotfix,
|
|
118
|
+
root,
|
|
119
|
+
repositoryRoot: gitRoot,
|
|
120
|
+
};
|
|
121
|
+
writeSaveReport(summary);
|
|
122
|
+
console.log('Treeseed save completed successfully.');
|
|
123
|
+
console.log(`Branch: ${branch}`);
|
|
124
|
+
console.log(`Environment scope: ${scope}`);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { applyTreeseedEnvironmentToProcess, assertTreeseedCommandEnvironment } from '../operations/services/config-runtime.js';
|
|
4
|
+
import { createBranchPreviewDeployTarget, deployTargetLabel, ensureGeneratedWranglerConfig, finalizeDeploymentState, printDeploySummary, provisionCloudflareResources, runRemoteD1Migrations, syncCloudflareSecrets, validateDeployPrerequisites, } from '../operations/services/deploy.js';
|
|
5
|
+
import { createFeatureBranchFromStaging, pushBranch } from '../operations/services/git-workflow.js';
|
|
6
|
+
import { packageScriptPath, resolveWranglerBin } from '../operations/services/runtime-tools.js';
|
|
7
|
+
function parseArgs(argv) {
|
|
8
|
+
const parsed = {
|
|
9
|
+
branchName: null,
|
|
10
|
+
preview: false,
|
|
11
|
+
};
|
|
12
|
+
for (const current of argv) {
|
|
13
|
+
if (current === '--preview') {
|
|
14
|
+
parsed.preview = true;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (!parsed.branchName) {
|
|
18
|
+
parsed.branchName = current;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`Unknown start argument: ${current}`);
|
|
22
|
+
}
|
|
23
|
+
if (!parsed.branchName) {
|
|
24
|
+
throw new Error('Usage: treeseed start <branch-name> [--preview]');
|
|
25
|
+
}
|
|
26
|
+
return parsed;
|
|
27
|
+
}
|
|
28
|
+
function runNodeScript(scriptPath, scriptArgs = [], cwd) {
|
|
29
|
+
const result = spawnSync(process.execPath, [scriptPath, ...scriptArgs], {
|
|
30
|
+
stdio: 'inherit',
|
|
31
|
+
cwd,
|
|
32
|
+
env: { ...process.env },
|
|
33
|
+
});
|
|
34
|
+
if (result.status !== 0) {
|
|
35
|
+
process.exit(result.status ?? 1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function runWranglerDeploy(configPath, cwd) {
|
|
39
|
+
const result = spawnSync(process.execPath, [resolveWranglerBin(), 'deploy', '--config', configPath], {
|
|
40
|
+
stdio: 'inherit',
|
|
41
|
+
cwd,
|
|
42
|
+
env: { ...process.env },
|
|
43
|
+
});
|
|
44
|
+
if (result.status !== 0) {
|
|
45
|
+
process.exit(result.status ?? 1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const options = parseArgs(process.argv.slice(2));
|
|
49
|
+
const tenantRoot = process.cwd();
|
|
50
|
+
const result = createFeatureBranchFromStaging(tenantRoot, options.branchName);
|
|
51
|
+
pushBranch(result.repoDir, options.branchName, { setUpstream: true });
|
|
52
|
+
if (!options.preview) {
|
|
53
|
+
console.log(`Created feature branch ${options.branchName} from staging.`);
|
|
54
|
+
console.log('Preview mode is disabled. Use local development for this branch.');
|
|
55
|
+
process.exit(0);
|
|
56
|
+
}
|
|
57
|
+
applyTreeseedEnvironmentToProcess({ tenantRoot, scope: 'staging', override: true });
|
|
58
|
+
assertTreeseedCommandEnvironment({ tenantRoot, scope: 'staging', purpose: 'deploy' });
|
|
59
|
+
validateDeployPrerequisites(tenantRoot, { requireRemote: true });
|
|
60
|
+
const target = createBranchPreviewDeployTarget(options.branchName);
|
|
61
|
+
const summary = provisionCloudflareResources(tenantRoot, { target });
|
|
62
|
+
printDeploySummary(summary);
|
|
63
|
+
const { wranglerPath } = ensureGeneratedWranglerConfig(tenantRoot, { target });
|
|
64
|
+
syncCloudflareSecrets(tenantRoot, { target });
|
|
65
|
+
runRemoteD1Migrations(tenantRoot, { target });
|
|
66
|
+
runNodeScript(packageScriptPath('tenant-build'), [], tenantRoot);
|
|
67
|
+
runWranglerDeploy(wranglerPath, tenantRoot);
|
|
68
|
+
const state = finalizeDeploymentState(tenantRoot, { target });
|
|
69
|
+
console.log(`Treeseed start preview completed for ${options.branchName}.`);
|
|
70
|
+
console.log(`Target: ${deployTargetLabel(target)}`);
|
|
71
|
+
console.log(`Preview URL: ${state.lastDeployedUrl}`);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { sortWorkspacePackages, workspacePackages, run } from '../operations/services/workspace-tools.js';
|
|
2
|
+
const packages = sortWorkspacePackages(workspacePackages());
|
|
3
|
+
for (const pkg of packages) {
|
|
4
|
+
if (typeof pkg.packageJson.scripts?.['test:unit'] === 'string') {
|
|
5
|
+
run('npm', ['run', 'test:unit'], { cwd: pkg.dir });
|
|
6
|
+
continue;
|
|
7
|
+
}
|
|
8
|
+
if (typeof pkg.packageJson.scripts?.test === 'string') {
|
|
9
|
+
run('npm', ['run', 'test'], { cwd: pkg.dir });
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SdkModelDefinition, SdkModelFieldBinding, SdkSortSpec, SdkFilterCondition } from './sdk-types.ts';
|
|
2
|
+
export declare function validateModelFieldAliases(definition: SdkModelDefinition): void;
|
|
3
|
+
export declare function resolveModelField(definition: SdkModelDefinition, requestedField: string): SdkModelFieldBinding;
|
|
4
|
+
export declare function readCanonicalFieldValue(definition: SdkModelDefinition, record: Record<string, unknown>, canonicalField: string): unknown;
|
|
5
|
+
export declare function normalizeRecordToCanonicalShape(definition: SdkModelDefinition, record: Record<string, unknown>): Record<string, unknown>;
|
|
6
|
+
export declare function normalizeMutationData(definition: SdkModelDefinition, data: Record<string, unknown>): Record<string, unknown>;
|
|
7
|
+
export declare function canonicalizeFrontmatter(definition: SdkModelDefinition, frontmatter: Record<string, unknown>, updates?: Record<string, unknown>): {
|
|
8
|
+
[x: string]: unknown;
|
|
9
|
+
};
|
|
10
|
+
export declare function normalizeFilterFields(definition: SdkModelDefinition, filters?: SdkFilterCondition[]): SdkFilterCondition[];
|
|
11
|
+
export declare function normalizeSortFields(definition: SdkModelDefinition, sort?: SdkSortSpec[]): SdkSortSpec[];
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveAliasedField
|
|
3
|
+
} from "./field-aliases.js";
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
function normalizeFieldName(value) {
|
|
8
|
+
return value.trim().toLowerCase();
|
|
9
|
+
}
|
|
10
|
+
function readPathValue(source, path) {
|
|
11
|
+
const normalizedPath = path.replace(/^\$\.?/u, "");
|
|
12
|
+
if (!normalizedPath) {
|
|
13
|
+
return void 0;
|
|
14
|
+
}
|
|
15
|
+
return normalizedPath.split(".").reduce((current, segment) => {
|
|
16
|
+
if (!isRecord(current)) {
|
|
17
|
+
return void 0;
|
|
18
|
+
}
|
|
19
|
+
return current[segment];
|
|
20
|
+
}, source);
|
|
21
|
+
}
|
|
22
|
+
function readCandidateValue(record, candidate) {
|
|
23
|
+
if (candidate in record) {
|
|
24
|
+
return record[candidate];
|
|
25
|
+
}
|
|
26
|
+
if (isRecord(record.frontmatter) && candidate in record.frontmatter) {
|
|
27
|
+
return record.frontmatter[candidate];
|
|
28
|
+
}
|
|
29
|
+
return readPathValue(record, candidate);
|
|
30
|
+
}
|
|
31
|
+
function normalizeFieldBinding(binding, canonicalKey) {
|
|
32
|
+
const aliases = [...new Set((binding.aliases ?? []).map(normalizeFieldName).filter(Boolean))];
|
|
33
|
+
const contentKeys = [...new Set((binding.contentKeys ?? []).filter(Boolean))];
|
|
34
|
+
const dbColumns = [...new Set((binding.dbColumns ?? []).filter(Boolean))];
|
|
35
|
+
const payloadPaths = [...new Set((binding.payloadPaths ?? []).filter(Boolean))];
|
|
36
|
+
return {
|
|
37
|
+
...binding,
|
|
38
|
+
key: canonicalKey,
|
|
39
|
+
aliases,
|
|
40
|
+
contentKeys,
|
|
41
|
+
dbColumns,
|
|
42
|
+
payloadPaths
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function validateModelFieldAliases(definition) {
|
|
46
|
+
const seen = /* @__PURE__ */ new Map();
|
|
47
|
+
for (const [canonicalKey, rawBinding] of Object.entries(definition.fields ?? {})) {
|
|
48
|
+
if (!canonicalKey.trim()) {
|
|
49
|
+
throw new Error(`Model "${definition.name}" includes an empty canonical field key.`);
|
|
50
|
+
}
|
|
51
|
+
const binding = normalizeFieldBinding(rawBinding, canonicalKey);
|
|
52
|
+
for (const candidate of [canonicalKey, ...binding.aliases ?? []]) {
|
|
53
|
+
const normalized = normalizeFieldName(candidate);
|
|
54
|
+
const owner = seen.get(normalized);
|
|
55
|
+
if (owner && owner !== canonicalKey) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Model "${definition.name}" reuses field alias "${candidate}" for both "${owner}" and "${canonicalKey}".`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
seen.set(normalized, canonicalKey);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (!(definition.pickField in (definition.fields ?? {}))) {
|
|
64
|
+
throw new Error(`Model "${definition.name}" pickField "${definition.pickField}" does not reference a defined field.`);
|
|
65
|
+
}
|
|
66
|
+
for (const fieldName of definition.filterableFields ?? []) {
|
|
67
|
+
if (!(fieldName in (definition.fields ?? {}))) {
|
|
68
|
+
throw new Error(`Model "${definition.name}" filterable field "${fieldName}" is not defined in fields.`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
for (const fieldName of definition.sortableFields ?? []) {
|
|
72
|
+
if (!(fieldName in (definition.fields ?? {}))) {
|
|
73
|
+
throw new Error(`Model "${definition.name}" sortable field "${fieldName}" is not defined in fields.`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function resolveModelField(definition, requestedField) {
|
|
78
|
+
const registry = Object.fromEntries(
|
|
79
|
+
Object.entries(definition.fields ?? {}).map(([canonicalKey, rawBinding]) => [
|
|
80
|
+
canonicalKey,
|
|
81
|
+
normalizeFieldBinding(rawBinding, canonicalKey)
|
|
82
|
+
])
|
|
83
|
+
);
|
|
84
|
+
return resolveAliasedField(registry, requestedField);
|
|
85
|
+
}
|
|
86
|
+
function readCanonicalFieldValue(definition, record, canonicalField) {
|
|
87
|
+
const binding = resolveModelField(definition, canonicalField);
|
|
88
|
+
for (const candidate of [
|
|
89
|
+
binding.key,
|
|
90
|
+
...binding.contentKeys ?? [],
|
|
91
|
+
...binding.dbColumns ?? [],
|
|
92
|
+
...binding.payloadPaths ?? [],
|
|
93
|
+
...binding.aliases ?? []
|
|
94
|
+
]) {
|
|
95
|
+
const value = readCandidateValue(record, candidate);
|
|
96
|
+
if (value !== void 0) {
|
|
97
|
+
return binding.normalize ? binding.normalize(value) : value;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return void 0;
|
|
101
|
+
}
|
|
102
|
+
function normalizeRecordToCanonicalShape(definition, record) {
|
|
103
|
+
const normalized = { ...record };
|
|
104
|
+
for (const canonicalField of Object.keys(definition.fields ?? {})) {
|
|
105
|
+
const value = readCanonicalFieldValue(definition, record, canonicalField);
|
|
106
|
+
if (value !== void 0) {
|
|
107
|
+
normalized[canonicalField] = value;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return normalized;
|
|
111
|
+
}
|
|
112
|
+
function normalizeMutationData(definition, data) {
|
|
113
|
+
const next = { ...data };
|
|
114
|
+
for (const key of Object.keys(data)) {
|
|
115
|
+
const binding = resolveModelField(definition, key);
|
|
116
|
+
next[binding.key] = binding.normalize ? binding.normalize(data[key]) : data[key];
|
|
117
|
+
if (binding.key !== key) {
|
|
118
|
+
delete next[key];
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return next;
|
|
122
|
+
}
|
|
123
|
+
function canonicalizeFrontmatter(definition, frontmatter, updates = {}) {
|
|
124
|
+
const normalizedExisting = normalizeRecordToCanonicalShape(definition, frontmatter);
|
|
125
|
+
const normalizedUpdates = normalizeMutationData(definition, updates);
|
|
126
|
+
const next = { ...frontmatter };
|
|
127
|
+
for (const [canonicalField, value] of Object.entries({ ...normalizedExisting, ...normalizedUpdates })) {
|
|
128
|
+
const binding = definition.fields[canonicalField];
|
|
129
|
+
if (!binding) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const writeKey = binding.writeContentKey ?? binding.contentKeys?.[0] ?? canonicalField;
|
|
133
|
+
for (const key of [binding.key, ...binding.contentKeys ?? [], ...binding.aliases ?? []]) {
|
|
134
|
+
if (key !== writeKey) {
|
|
135
|
+
delete next[key];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
next[writeKey] = value;
|
|
139
|
+
}
|
|
140
|
+
return next;
|
|
141
|
+
}
|
|
142
|
+
function normalizeFilterFields(definition, filters = []) {
|
|
143
|
+
return filters.map((filter) => {
|
|
144
|
+
const binding = resolveModelField(definition, filter.field);
|
|
145
|
+
return {
|
|
146
|
+
...filter,
|
|
147
|
+
field: binding.key
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function normalizeSortFields(definition, sort = []) {
|
|
152
|
+
return sort.map((entry) => {
|
|
153
|
+
const binding = resolveModelField(definition, entry.field);
|
|
154
|
+
return {
|
|
155
|
+
...entry,
|
|
156
|
+
field: binding.key
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
export {
|
|
161
|
+
canonicalizeFrontmatter,
|
|
162
|
+
normalizeFilterFields,
|
|
163
|
+
normalizeMutationData,
|
|
164
|
+
normalizeRecordToCanonicalShape,
|
|
165
|
+
normalizeSortFields,
|
|
166
|
+
readCanonicalFieldValue,
|
|
167
|
+
resolveModelField,
|
|
168
|
+
validateModelFieldAliases
|
|
169
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { SdkContentEntry, SdkFilterCondition, SdkModelDefinition, SdkSortSpec } from './sdk-types.ts';
|
|
2
|
+
export declare function matchesFilter(entry: SdkContentEntry | Record<string, unknown>, filter: SdkFilterCondition, definition?: SdkModelDefinition): boolean;
|
|
3
|
+
export declare function applyFilters<T extends SdkContentEntry | Record<string, unknown>>(items: T[], filters?: SdkFilterCondition[], definition?: SdkModelDefinition): T[];
|
|
4
|
+
export declare function applySort<T extends SdkContentEntry | Record<string, unknown>>(items: T[], sort?: SdkSortSpec[], definition?: SdkModelDefinition): T[];
|
package/dist/sdk-filters.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readCanonicalFieldValue } from "./sdk-fields.js";
|
|
1
2
|
function toArray(value) {
|
|
2
3
|
if (Array.isArray(value)) {
|
|
3
4
|
return value;
|
|
@@ -11,19 +12,13 @@ function compareScalar(left, right) {
|
|
|
11
12
|
if (typeof left === "number" && typeof right === "number") {
|
|
12
13
|
return left - right;
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
function getFieldValue(entry, field) {
|
|
17
|
-
if (field in entry) {
|
|
18
|
-
return entry[field];
|
|
15
|
+
if (left instanceof Date || right instanceof Date) {
|
|
16
|
+
return new Date(String(left ?? 0)).valueOf() - new Date(String(right ?? 0)).valueOf();
|
|
19
17
|
}
|
|
20
|
-
|
|
21
|
-
return entry.frontmatter[field];
|
|
22
|
-
}
|
|
23
|
-
return void 0;
|
|
18
|
+
return String(left ?? "").localeCompare(String(right ?? ""));
|
|
24
19
|
}
|
|
25
|
-
function matchesFilter(entry, filter) {
|
|
26
|
-
const fieldValue =
|
|
20
|
+
function matchesFilter(entry, filter, definition) {
|
|
21
|
+
const fieldValue = definition ? readCanonicalFieldValue(definition, entry, filter.field) : entry[filter.field];
|
|
27
22
|
switch (filter.op) {
|
|
28
23
|
case "eq":
|
|
29
24
|
return fieldValue === filter.value;
|
|
@@ -52,17 +47,19 @@ function matchesFilter(entry, filter) {
|
|
|
52
47
|
return false;
|
|
53
48
|
}
|
|
54
49
|
}
|
|
55
|
-
function applyFilters(items, filters = []) {
|
|
56
|
-
return items.filter((item) => filters.every((filter) => matchesFilter(item, filter)));
|
|
50
|
+
function applyFilters(items, filters = [], definition) {
|
|
51
|
+
return items.filter((item) => filters.every((filter) => matchesFilter(item, filter, definition)));
|
|
57
52
|
}
|
|
58
|
-
function applySort(items, sort = []) {
|
|
53
|
+
function applySort(items, sort = [], definition) {
|
|
59
54
|
if (sort.length === 0) {
|
|
60
55
|
return items;
|
|
61
56
|
}
|
|
62
57
|
return [...items].sort((left, right) => {
|
|
63
58
|
for (const spec of sort) {
|
|
64
59
|
const direction = spec.direction === "asc" ? 1 : -1;
|
|
65
|
-
const
|
|
60
|
+
const leftValue = definition ? readCanonicalFieldValue(definition, left, spec.field) : left[spec.field];
|
|
61
|
+
const rightValue = definition ? readCanonicalFieldValue(definition, right, spec.field) : right[spec.field];
|
|
62
|
+
const comparison = compareScalar(leftValue, rightValue);
|
|
66
63
|
if (comparison !== 0) {
|
|
67
64
|
return comparison * direction;
|
|
68
65
|
}
|