@stacksjs/buddy 0.70.45 → 0.70.54
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/LICENSE.md +21 -0
- package/dist/cli.js +2 -2
- package/dist/index.js +2487 -448
- package/dist/src/commands/cd.d.ts +20 -0
- package/dist/src/commands/deploy.d.ts +103 -0
- package/dist/src/commands/dev.d.ts +7 -0
- package/dist/src/commands/features.d.ts +172 -0
- package/dist/src/commands/index.d.ts +4 -0
- package/dist/src/commands/migrate-project.d.ts +2 -0
- package/dist/src/commands/serve.d.ts +25 -0
- package/dist/src/migrators/index.d.ts +12 -0
- package/dist/src/migrators/laravel/index.d.ts +2 -0
- package/dist/src/migrators/laravel/migrations.d.ts +52 -0
- package/dist/src/migrators/laravel/models.d.ts +34 -0
- package/dist/src/migrators/rails/index.d.ts +2 -0
- package/dist/src/migrators/types.d.ts +44 -0
- package/package.json +5 -4
- package/dist/package.json +0 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { CLI } from '@stacksjs/types';
|
|
2
|
+
/**
|
|
3
|
+
* `buddy cd <name>` — resolve the absolute path of a Stacks project by name.
|
|
4
|
+
*
|
|
5
|
+
* A child process can't mutate its parent shell's cwd, so this command
|
|
6
|
+
* prints the resolved path to stdout rather than calling `chdir()`. Users
|
|
7
|
+
* who want true `cd` behavior add a shell wrapper to their rc:
|
|
8
|
+
*
|
|
9
|
+
* bcd() { eval "$(buddy cd "$1" --eval)"; }
|
|
10
|
+
*
|
|
11
|
+
* Then `bcd my-project` actually changes the shell's directory.
|
|
12
|
+
*
|
|
13
|
+
* The underlying scan (`findStacksProjects`) excludes `~/Documents`,
|
|
14
|
+
* `~/Library`, `~/Pictures`, `~/.Trash` by default. Users with projects
|
|
15
|
+
* outside `~/` can point the scan at a specific root via `--root <dir>`
|
|
16
|
+
* or the `STACKS_PROJECTS_ROOT` env var.
|
|
17
|
+
*
|
|
18
|
+
* stacksjs/stacks#527.
|
|
19
|
+
*/
|
|
20
|
+
export declare function cd(buddy: CLI): void;
|
|
@@ -1,2 +1,105 @@
|
|
|
1
1
|
import type { CLI } from '@stacksjs/types';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve (and decrypt) the deploy-target's environment file into a flat
|
|
4
|
+
* key/value map, so its values can be shipped to the server as each site's
|
|
5
|
+
* systemd `.env` content.
|
|
6
|
+
*
|
|
7
|
+
* ts-cloud's `buildSiteDeployScript` treats `site.env` as the COMPLETE
|
|
8
|
+
* content of the deployed `.env` — it doesn't read or merge in anything
|
|
9
|
+
* from the packaged release tarball (ts-cloud is a generic deploy tool; it
|
|
10
|
+
* has no idea `.env.production`/dotenvx encryption exist, that's entirely a
|
|
11
|
+
* Stacks convention). Left unaddressed, every Hetzner site deploys with
|
|
12
|
+
* ONLY whatever's in that site's own `env` override (often nothing at all)
|
|
13
|
+
* — confirmed against a real deploy (stacksjs/status#1 Phase 9): the `main`
|
|
14
|
+
* site (no `env` override) came up logging "loaded 0 variables from .env",
|
|
15
|
+
* and `api` (which only declares `{ HOST, APP_ENV }` to force the loopback
|
|
16
|
+
* bind) came up with just those 2 keys and none of its real production
|
|
17
|
+
* config, failing config validation on the still-`encrypted:...` APP_ENV
|
|
18
|
+
* ciphertext it never had a chance to decrypt (no DOTENV_PRIVATE_KEY_* in
|
|
19
|
+
* that 2-key set).
|
|
20
|
+
*
|
|
21
|
+
* Returns `{}` (not an error) when the file doesn't exist or fails to
|
|
22
|
+
* parse — an app with no `.env.production` yet shouldn't block deploying
|
|
23
|
+
* with whatever `site.env` overrides it does have.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveDeployEnvValues(environment: 'production' | 'staging' | 'development'): Promise<Record<string, string>>;
|
|
26
|
+
/**
|
|
27
|
+
* Merge the deploy-target's resolved env values underneath each site's own
|
|
28
|
+
* explicit `env` overrides, stripping a general `PORT` when the site
|
|
29
|
+
* declares its own `port` (the generated systemd unit already sets
|
|
30
|
+
* `Environment=PORT=${site.port}` — see buildSiteDeployScript in ts-cloud —
|
|
31
|
+
* so a leftover PORT in the shipped `.env` would otherwise silently win
|
|
32
|
+
* over it once the app's own dotenv loading applies file values on top of
|
|
33
|
+
* the process env).
|
|
34
|
+
*/
|
|
35
|
+
export declare function mergeSiteDeployEnv(sites: Record<string, any>, resolvedDeployEnv: Record<string, string>): Record<string, any>;
|
|
36
|
+
/**
|
|
37
|
+
* Make the site model environment-aware. For a non-production environment that
|
|
38
|
+
* declares a `domainPrefix` (staging → `staging`, development → `dev`), every
|
|
39
|
+
* site's public domain becomes `<prefix>.<domain>`, and URL values that point at
|
|
40
|
+
* those hosts (APP_URL, OAuth redirect URLs, redirect targets, …) are rewritten
|
|
41
|
+
* to match — so one config drives prod + staging + dev from their own branches
|
|
42
|
+
* without duplicating site blocks. Only `//<host>` URL occurrences are rewritten;
|
|
43
|
+
* bare `user@host` (e.g. mail identities) is left alone. Production is untouched.
|
|
44
|
+
*/
|
|
45
|
+
export declare function applyEnvironmentToSites(sites: Record<string, any>, environment: string, config: any): Record<string, any>;
|
|
46
|
+
/**
|
|
47
|
+
* Loopback-bound server-app sites (e.g. the `api` site: env.HOST=127.0.0.1,
|
|
48
|
+
* reached only through `buddy serve`'s same-origin /api proxy on :3000 —
|
|
49
|
+
* stacksjs/stacks#1950) must NOT have their port opened to the internet.
|
|
50
|
+
* ts-cloud's Hetzner provisioning opens EVERY numeric `site.port` to
|
|
51
|
+
* 0.0.0.0/0 + ::/0 (collectUpstreamPorts → buildHetznerFirewallRules), which
|
|
52
|
+
* would leave only the process bind between the public internet and the full
|
|
53
|
+
* bun-router API. Hand the provision step a copy of the config with those
|
|
54
|
+
* ports stripped — the unmodified config still drives deployAllComputeSites,
|
|
55
|
+
* so the systemd unit (ExecStart, Environment=PORT) is unaffected.
|
|
56
|
+
*
|
|
57
|
+
* Domain-less sites only: a loopback site WITH a domain feeds the rpx
|
|
58
|
+
* gateway's route table (which proxies to 127.0.0.1:port on-box), so its
|
|
59
|
+
* port declaration is left alone.
|
|
60
|
+
*/
|
|
61
|
+
export declare function scrubLoopbackSitePortsForFirewall(tsCloudConfig: any): any;
|
|
62
|
+
/**
|
|
63
|
+
* Everything is MERGE-based so a shared mail server keeps every other tenant's
|
|
64
|
+
* domains, keys, users, and forward rules untouched. Best-effort — a hiccup is
|
|
65
|
+
* logged, never fails the release. Returns what the DNS step needs (mail host +
|
|
66
|
+
* DKIM public key), or null when there is nothing to reconcile / it failed.
|
|
67
|
+
*/
|
|
68
|
+
export declare function provisionMailTenant(ip: string, logger: typeof log): Promise<MailTenantResult | null>;
|
|
69
|
+
/**
|
|
70
|
+
* Publish the mail DNS for a hosted domain via Porkbun (idempotent delete+create
|
|
71
|
+
* per record): MX → the mail host, SPF authorizing the box IP, the domain's DKIM
|
|
72
|
+
* public key at `mail._domainkey`, and a DMARC policy. Best-effort — logged, not
|
|
73
|
+
* thrown. No-op without Porkbun credentials (the records are printed to add by
|
|
74
|
+
* hand). MX targets the shared mail host (`mail.stacksjs.com`), so no per-domain
|
|
75
|
+
* mail A record or extra TLS SAN is needed.
|
|
76
|
+
*/
|
|
77
|
+
export declare function reconcileMailDns(res: MailTenantResult, ip: string, logger: typeof log): Promise<void>;
|
|
2
78
|
export declare function deploy(buddy: CLI): void;
|
|
79
|
+
/**
|
|
80
|
+
* Use console.log for clean output without timestamps
|
|
81
|
+
* @defaultValue
|
|
82
|
+
* ```ts
|
|
83
|
+
* {
|
|
84
|
+
* info: (...args: any[]) => unknown,
|
|
85
|
+
* success: (...args: any[]) => unknown,
|
|
86
|
+
* warn: (...args: any[]) => unknown,
|
|
87
|
+
* error: (...args: any[]) => unknown,
|
|
88
|
+
* debug: (...args: any[]) => unknown
|
|
89
|
+
* }
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
declare const log: {
|
|
93
|
+
info: (...args: any[]) => unknown;
|
|
94
|
+
success: (...args: any[]) => unknown;
|
|
95
|
+
warn: (...args: any[]) => unknown;
|
|
96
|
+
error: (...args: any[]) => unknown;
|
|
97
|
+
debug: (...args: any[]) => unknown
|
|
98
|
+
};
|
|
99
|
+
/** What a mail-tenant reconcile resolved + provisioned, for the DNS step. */
|
|
100
|
+
export declare interface MailTenantResult {
|
|
101
|
+
domain: string
|
|
102
|
+
mailHost: string
|
|
103
|
+
dkimPubB64?: string
|
|
104
|
+
created: Array<{ address: string, password: string }>
|
|
105
|
+
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
import type { CLI, DevOptions } from '@stacksjs/types';
|
|
2
2
|
export declare function dev(buddy: CLI): void;
|
|
3
3
|
export declare function startDevelopmentServer(_options: DevOptions, _startTime?: number): Promise<void>;
|
|
4
|
+
declare type DevelopmentRpx = typeof import('@stacksjs/rpx');
|
|
5
|
+
type RpxProxySpec = {
|
|
6
|
+
id: string
|
|
7
|
+
from: string
|
|
8
|
+
to: string
|
|
9
|
+
pathRewrites?: Array<{ from: string, to: string, stripPrefix?: boolean }>
|
|
10
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import type { CLI } from '@stacksjs/types';
|
|
2
|
+
/**
|
|
3
|
+
* Parse a migration filename like `0000000045-create-posts-table.sql` or
|
|
4
|
+
* `0000000085-alter-posts-author_id.sql` to extract the table name it
|
|
5
|
+
* acts on. Returns `null` for filenames that don't match the
|
|
6
|
+
* recognised `create-<table>-table` / `alter-<table>-` shapes — those
|
|
7
|
+
* pass through the gate unchanged.
|
|
8
|
+
*
|
|
9
|
+
* Index migrations (`create-<table>_<col>_unique-index-in-<table>.sql`)
|
|
10
|
+
* use the trailing `-in-<table>.sql` segment as the source of truth
|
|
11
|
+
* since the leading segment includes the index name. The other
|
|
12
|
+
* forms read the table from the segment immediately after the verb.
|
|
13
|
+
*/
|
|
14
|
+
export declare function migrationTable(filename: string): string | null;
|
|
15
|
+
/**
|
|
16
|
+
* Returns the feature that owns the given migration filename, or `null`
|
|
17
|
+
* if the migration isn't claimed by any feature (in which case it
|
|
18
|
+
* always runs). Used by the migration runner's gating pass.
|
|
19
|
+
*/
|
|
20
|
+
export declare function migrationFeature(filename: string): FeatureName | null;
|
|
21
|
+
/**
|
|
22
|
+
* Returns the subset of a feature's manifest paths that currently exist
|
|
23
|
+
* on disk under `root` (defaults to `projectPath()`). Used by both the
|
|
24
|
+
* uninstall delete and the doctor orphan check.
|
|
25
|
+
*/
|
|
26
|
+
export declare function featurePathsPresent(feature: FeatureName, root?: string): string[];
|
|
27
|
+
/**
|
|
28
|
+
* Recursively delete every file/dir listed in the feature's manifest under
|
|
29
|
+
* `root` (defaults to `projectPath()`). Missing entries are skipped
|
|
30
|
+
* silently — the operation is safe to re-run. Returns the list of paths
|
|
31
|
+
* actually removed so the caller can print a useful summary.
|
|
32
|
+
*/
|
|
33
|
+
export declare function deleteFeatureFiles(feature: FeatureName, root?: string): Promise<string[]>;
|
|
34
|
+
/**
|
|
35
|
+
* Copy every path listed in the feature's manifest from the framework
|
|
36
|
+
* defaults tree (`storage/framework/defaults/<path>`) into the project
|
|
37
|
+
* root (`<project>/<path>`). Missing source entries are skipped — not
|
|
38
|
+
* every feature owns every manifest slot (e.g. `cms` doesn't ship a
|
|
39
|
+
* `resources/components/Dashboard/Commerce/` dir, so that entry is
|
|
40
|
+
* absent from its source). Existing target paths are also skipped by
|
|
41
|
+
* default so re-running install on an existing project is idempotent.
|
|
42
|
+
*
|
|
43
|
+
* Returns the list of paths actually copied. See stacksjs/stacks#1854.
|
|
44
|
+
*/
|
|
45
|
+
export declare function copyFeatureFiles(feature: FeatureName, options?: CopyFeatureFilesOptions): Promise<{ copied: string[], skipped: string[] }>;
|
|
46
|
+
/**
|
|
47
|
+
* Flip the top-level `enabled` field in `config/<feature>.ts` to the
|
|
48
|
+
* desired value. Returns:
|
|
49
|
+
*
|
|
50
|
+
* - 'created' — file did not exist and we scaffolded it (install only)
|
|
51
|
+
* - 'flipped' — file existed; `enabled` was on the opposite value
|
|
52
|
+
* - 'unchanged' — file existed; `enabled` already matched
|
|
53
|
+
* - 'missing' — file did not exist and `createIfMissing` was false
|
|
54
|
+
* (uninstall path: nothing to do, feature is already off)
|
|
55
|
+
*
|
|
56
|
+
* Pass `options.root` to target a project directory other than the
|
|
57
|
+
* caller's working tree — `./buddy new --minimal` uses this to disable
|
|
58
|
+
* features in the freshly-cloned project path before the user has cd'd
|
|
59
|
+
* into it.
|
|
60
|
+
*/
|
|
61
|
+
export declare function setFeatureEnabled(feature: FeatureName, enabled: boolean, options: { createIfMissing: boolean, root?: string }): Promise<SetFeatureEnabledOutcome>;
|
|
62
|
+
/**
|
|
63
|
+
* Disable every feature in `FEATURE_NAMES` at once — flips
|
|
64
|
+
* `config/<feature>.ts` enabled flags to `false` and removes the
|
|
65
|
+
* stamped scaffolding under the project root.
|
|
66
|
+
*
|
|
67
|
+
* Used by `./buddy new --minimal` to turn the kitchen-sink template
|
|
68
|
+
* `@stacksjs/gitit` clones into a bare-bones starter. Individual
|
|
69
|
+
* `<feature>:install` commands re-enable + re-stamp on demand.
|
|
70
|
+
*
|
|
71
|
+
* Pass `root` to target a project directory other than `projectPath()`;
|
|
72
|
+
* the create command uses this because `./buddy new` runs from the
|
|
73
|
+
* user's cwd while the freshly-cloned project lives at `<cwd>/<name>`.
|
|
74
|
+
*
|
|
75
|
+
* Safe to re-run — both halves of each per-feature step are idempotent.
|
|
76
|
+
*/
|
|
77
|
+
export declare function uninstallAllFeatures(options?: { root?: string }): Promise<UninstallAllFeaturesResult[]>;
|
|
78
|
+
export declare function features(buddy: CLI): void;
|
|
79
|
+
/**
|
|
80
|
+
* Feature install / uninstall commands.
|
|
81
|
+
*
|
|
82
|
+
* Each framework feature bundle (dashboard, commerce, cms, marketing,
|
|
83
|
+
* monitoring, realtime, queue) lives in its own `config/<feature>.ts`
|
|
84
|
+
* file. Running `./buddy <feature>:install` flips that file's top-level
|
|
85
|
+
* `enabled` to `true` (scaffolding the file from a starter template if it's
|
|
86
|
+
* missing). `./buddy <feature>:uninstall` flips the flag back to `false`
|
|
87
|
+
* AND removes the feature's stamped action/model/view files from the
|
|
88
|
+
* project (pass `--keep-files` to preserve them); the config file itself
|
|
89
|
+
* is preserved so any custom driver/credential settings survive a future
|
|
90
|
+
* reinstall.
|
|
91
|
+
*
|
|
92
|
+
* The framework loaders (`orm/index.ts` eager-load, `defaults/bootstrap.ts`
|
|
93
|
+
* route registration, action prefetch) consult `feature(name)` at boot and
|
|
94
|
+
* skip anything whose flag is off — so an app with only `auth` activated
|
|
95
|
+
* never pays the cost of importing 70+ Commerce models or registering
|
|
96
|
+
* hundreds of dashboard routes it doesn't use.
|
|
97
|
+
*
|
|
98
|
+
* Auth is intentionally not in this list: it has its own scaffolding
|
|
99
|
+
* pipeline (`buddy auth:setup`) which handles migrations + personal-access
|
|
100
|
+
* client setup beyond a simple `enabled` flip.
|
|
101
|
+
*
|
|
102
|
+
* Mirrors Laravel's `php artisan passport:install` / `horizon:install`
|
|
103
|
+
* pattern: features are inert dead code on disk until installed.
|
|
104
|
+
*/
|
|
105
|
+
export declare const FEATURE_NAMES: readonly ['dashboard', 'commerce', 'cms', 'marketing', 'monitoring', 'realtime', 'queue'];
|
|
106
|
+
/**
|
|
107
|
+
* Per-feature stamped file/directory manifest. Paths are relative to the
|
|
108
|
+
* project root and mirror the layout that `./buddy new` lays down. Entries
|
|
109
|
+
* ending in `/` are directory trees (recursive remove on uninstall); bare
|
|
110
|
+
* paths are single files.
|
|
111
|
+
*
|
|
112
|
+
* Manifests intentionally overlap where features share scaffolding —
|
|
113
|
+
* `dashboard` claims the umbrella `app/Actions/Dashboard/` even though
|
|
114
|
+
* `app/Actions/Dashboard/Content/` is also claimed by `cms`. Both the
|
|
115
|
+
* uninstall delete and the doctor orphan check are idempotent
|
|
116
|
+
* (already-gone paths are skipped silently), so the overlap is safe.
|
|
117
|
+
*
|
|
118
|
+
* Adding a new file to one of these directories does **not** require a
|
|
119
|
+
* manifest update — directory entries are recursive. Only add an entry
|
|
120
|
+
* when a feature introduces a new top-level path the framework didn't
|
|
121
|
+
* already claim.
|
|
122
|
+
*/
|
|
123
|
+
export declare const FEATURE_FILES: {
|
|
124
|
+
cms: readonly ['app/Actions/Cms/', 'app/Actions/Dashboard/Content/', 'app/Models/Content/', 'app/Models/Tag.ts', 'app/Models/Comment.ts', 'resources/views/dashboard/content/'];
|
|
125
|
+
commerce: readonly ['app/Actions/Commerce/', 'app/Actions/Dashboard/Commerce/', 'app/Models/commerce/', 'resources/components/Dashboard/Commerce/', 'resources/views/dashboard/commerce/'];
|
|
126
|
+
dashboard: readonly ['app/Actions/Dashboard/', 'resources/components/Dashboard/', 'resources/views/dashboard/', 'routes/dashboard.ts', 'routes/dashboard-api.ts'];
|
|
127
|
+
marketing: readonly ['app/Actions/Dashboard/Marketing/', 'app/Models/Campaign.ts', 'app/Models/CampaignSend.ts', 'app/Models/EmailList.ts', 'app/Models/EmailListSubscriber.ts', 'app/Models/SocialPost.ts', 'resources/components/Marketing/', 'resources/views/dashboard/marketing/'];
|
|
128
|
+
monitoring: readonly ['app/Actions/Monitoring/', 'app/Actions/TestErrorAction.ts', 'app/Models/Error.ts', 'functions/monitoring/', 'resources/views/dashboard/monitoring/', 'resources/views/dashboard/errors/'];
|
|
129
|
+
realtime: readonly ['app/Actions/Realtime/', 'app/Actions/Dashboard/Realtime/', 'app/Models/realtime/', 'app/Broadcasts/', 'functions/realtime/', 'resources/views/dashboard/realtime/'];
|
|
130
|
+
queue: readonly ['app/Actions/Queue/', 'app/Actions/Dashboard/Jobs/', 'app/Jobs/', 'app/Models/Job.ts', 'app/Models/FailedJob.ts', 'functions/jobs.ts', 'resources/views/dashboard/queue/', 'resources/views/dashboard/jobs/']
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Per-feature database table ownership (stacksjs/stacks#1854).
|
|
134
|
+
*
|
|
135
|
+
* Stacks generates SQL migrations from model files, so each feature's
|
|
136
|
+
* tables map 1:1 with the models in its `FEATURE_FILES.app/Models/...`
|
|
137
|
+
* entries. Listed here explicitly rather than derived at runtime so
|
|
138
|
+
* additions are visible in a single grep-able place and the migration
|
|
139
|
+
* gate doesn't depend on filesystem scanning at boot.
|
|
140
|
+
*
|
|
141
|
+
* The migration runner consults this when `config.<feature>.enabled =
|
|
142
|
+
* false`: matching `*-create-<table>-table.sql` (and `*-alter-<table>-*.sql`)
|
|
143
|
+
* files get hidden for the duration of the run, so a project that
|
|
144
|
+
* never installed CMS doesn't materialize `posts`, `pages`,
|
|
145
|
+
* `comments`, etc. on `./buddy migrate`.
|
|
146
|
+
*
|
|
147
|
+
* Tables on this list are scoped to a single feature. Tables shared
|
|
148
|
+
* across features (none today, but `categories` could end up here)
|
|
149
|
+
* should stay out of the manifest until that's resolved — the runner
|
|
150
|
+
* defaults to "run unless owned by a disabled feature".
|
|
151
|
+
*/
|
|
152
|
+
export declare const FEATURE_TABLES: {
|
|
153
|
+
cms: readonly ['posts', 'pages', 'comments', 'tags', 'authors', 'categories'];
|
|
154
|
+
commerce: readonly ['products', 'product_variants', 'product_units', 'manufacturers', 'orders', 'order_items', 'carts', 'cart_items', 'payments', 'payment_methods', 'payment_products', 'payment_transactions', 'customers', 'subscribers', 'subscriber_emails', 'subscriptions', 'gift_cards', 'coupons', 'transactions', 'reviews', 'drivers', 'delivery_routes', 'digital_deliveries', 'shipping_methods', 'shipping_rates', 'shipping_zones', 'license_keys', 'loyalty_points', 'loyalty_rewards', 'print_devices', 'receipts', 'tax_rates', 'waitlist_products', 'waitlist_restaurants'];
|
|
155
|
+
dashboard: readonly ['boards', 'board_columns', 'cards', 'card_labels', 'card_assignees', 'card_comments', 'labels', 'ci_run_states', 'ci_runner_samples', 'ci_runner_alert_states', 'requests', 'logs'];
|
|
156
|
+
marketing: never[];
|
|
157
|
+
monitoring: readonly ['errors'];
|
|
158
|
+
realtime: readonly ['websockets'];
|
|
159
|
+
queue: readonly ['jobs', 'failed_jobs']
|
|
160
|
+
};
|
|
161
|
+
export declare interface CopyFeatureFilesOptions {
|
|
162
|
+
force?: boolean
|
|
163
|
+
source?: string
|
|
164
|
+
target?: string
|
|
165
|
+
}
|
|
166
|
+
export declare interface UninstallAllFeaturesResult {
|
|
167
|
+
feature: FeatureName
|
|
168
|
+
configOutcome: SetFeatureEnabledOutcome
|
|
169
|
+
filesRemoved: string[]
|
|
170
|
+
}
|
|
171
|
+
export type FeatureName = (typeof FEATURE_NAMES)[number];
|
|
172
|
+
export type SetFeatureEnabledOutcome = 'created' | 'flipped' | 'unchanged' | 'missing';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './about';
|
|
2
2
|
export * from './auth';
|
|
3
3
|
export * from './build';
|
|
4
|
+
export * from './cd';
|
|
4
5
|
export * from './changelog';
|
|
5
6
|
export * from './clean';
|
|
6
7
|
export * from './cloud';
|
|
@@ -16,6 +17,7 @@ export * from './doctor';
|
|
|
16
17
|
export * from './domains';
|
|
17
18
|
export * from './email';
|
|
18
19
|
export * from './env';
|
|
20
|
+
export * from './features';
|
|
19
21
|
export * from './fresh';
|
|
20
22
|
export * from './generate';
|
|
21
23
|
export * from './http';
|
|
@@ -26,6 +28,7 @@ export * from './list';
|
|
|
26
28
|
export * from './mail';
|
|
27
29
|
export * from './make';
|
|
28
30
|
export * from './migrate';
|
|
31
|
+
export * from './migrate-project';
|
|
29
32
|
export * from './outdated';
|
|
30
33
|
export * from './phone';
|
|
31
34
|
export * from './ports';
|
|
@@ -39,6 +42,7 @@ export * from './saas';
|
|
|
39
42
|
export * from './schedule';
|
|
40
43
|
export * from './search';
|
|
41
44
|
export * from './seed';
|
|
45
|
+
export * from './serve';
|
|
42
46
|
export * from './setup';
|
|
43
47
|
export * from './sms';
|
|
44
48
|
export * from './telemetry';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CLI } from '@stacksjs/types';
|
|
2
|
+
/**` requests (and any non-GET/HEAD verb) are
|
|
3
|
+
* reverse-proxied to the API process — mirroring the dev views server — so
|
|
4
|
+
* scaffolded `fetch('/api/...')` calls behave identically in production
|
|
5
|
+
* (stacksjs/stacks#1950). The API runs as a separate process
|
|
6
|
+
* (core/actions/src/serve/api.ts), deployed as a second systemd service via
|
|
7
|
+
* the `api` site in config/cloud.ts. Override `API_URL` when the API lives
|
|
8
|
+
* on another host, or `PORT_API` when only the port differs.
|
|
9
|
+
*
|
|
10
|
+
* This is the entry the Hetzner deploy runs as a systemd service
|
|
11
|
+
* (`bun storage/framework/core/buddy/src/cli.ts serve`).
|
|
12
|
+
*/
|
|
13
|
+
export declare function serve(buddy: CLI): void;
|
|
14
|
+
/**
|
|
15
|
+
* `buddy serve:api` — boot the production API server (bun-router routes).
|
|
16
|
+
*
|
|
17
|
+
* The twin of `buddy serve`: where that serves the STX frontend, this runs the
|
|
18
|
+
* loopback API the frontend proxies `/api` + non-GET requests to. The entry
|
|
19
|
+
* (`@stacksjs/actions/serve/api`) is resolved through the module graph, so it
|
|
20
|
+
* works whether the framework is vendored at `storage/framework/core` OR only
|
|
21
|
+
* installed under `node_modules/@stacksjs/actions`. This keeps deployments from
|
|
22
|
+
* having to hardcode a `storage/framework/core/...` path in their `start`
|
|
23
|
+
* command — `./buddy serve:api` resolves the framework wherever it lives.
|
|
24
|
+
*/
|
|
25
|
+
export declare function serveApi(buddy: CLI): void;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Driver, MigrationReport, MigrateProjectRequest } from './types';
|
|
2
|
+
export type { MigrateProjectRequest, MigrationReport, ReportEntry } from './types';
|
|
3
|
+
export declare function runMigrator(req: MigrateProjectRequest): Promise<MigrationReport>;
|
|
4
|
+
/**
|
|
5
|
+
* Render a migration report as a Markdown checklist suitable for
|
|
6
|
+
* writing to `MIGRATION_REPORT.md` in the target project.
|
|
7
|
+
*/
|
|
8
|
+
export declare function renderReport(report: MigrationReport): string;
|
|
9
|
+
export declare const DRIVERS: {
|
|
10
|
+
laravel: unknown;
|
|
11
|
+
rails: unknown
|
|
12
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a Laravel migration file. Returns `null` when no
|
|
3
|
+
* `Schema::create(...)` block is found (e.g. drop migrations,
|
|
4
|
+
* data-only migrations, alter-table migrations — those are deferred
|
|
5
|
+
* to a separate emitter once the create paths land).
|
|
6
|
+
*/
|
|
7
|
+
export declare function parseLaravelMigration(source: string): ParsedMigration | null;
|
|
8
|
+
/**
|
|
9
|
+
* Lift Laravel's `YYYY_MM_DD_HHMMSS_create_<table>_table.php` filename
|
|
10
|
+
* into the `0000000NNN-create-<table>-table.sql` form the Stacks
|
|
11
|
+
* migration runner expects. The numeric prefix preserves ordering
|
|
12
|
+
* across the existing Stacks migrations directory — callers should
|
|
13
|
+
* pass a `sequence` that starts above whatever's already there.
|
|
14
|
+
*/
|
|
15
|
+
export declare function laravelFilenameToStacks(filename: string, sequence: number, table: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Laravel migration → Stacks SQL translator.
|
|
18
|
+
*
|
|
19
|
+
* Laravel migrations are PHP classes whose `up()` calls
|
|
20
|
+
* `Schema::create(table, fn (Blueprint $t) => { ... })`. We don't
|
|
21
|
+
* parse arbitrary PHP — we recognise the common Blueprint DSL by
|
|
22
|
+
* regex and emit a CREATE TABLE statement that the Stacks runner can
|
|
23
|
+
* consume.
|
|
24
|
+
*
|
|
25
|
+
* Strategy:
|
|
26
|
+
* 1. Find the `Schema::create('table', function (...) { BODY })` block.
|
|
27
|
+
* 2. Tokenise BODY line-by-line, each line a `$table->method(args)->modifier()...;` chain.
|
|
28
|
+
* 3. Map the leading method to a SQLite column type.
|
|
29
|
+
* 4. Translate modifiers (`->nullable()`, `->unique()`, `->default(...)`, `->index()`)
|
|
30
|
+
* into column constraints (or post-table CREATE INDEX statements).
|
|
31
|
+
*
|
|
32
|
+
* Anything unrecognised gets logged in the report as `// SKIPPED:` and
|
|
33
|
+
* the user can hand-port it.
|
|
34
|
+
*/
|
|
35
|
+
export declare interface ParsedMigration {
|
|
36
|
+
table: string
|
|
37
|
+
sql: string
|
|
38
|
+
skipped: string[]
|
|
39
|
+
}
|
|
40
|
+
declare interface Column {
|
|
41
|
+
name: string
|
|
42
|
+
type: string
|
|
43
|
+
nullable: boolean
|
|
44
|
+
unique: boolean
|
|
45
|
+
primaryKey: boolean
|
|
46
|
+
autoIncrement: boolean
|
|
47
|
+
defaultValue: string | null
|
|
48
|
+
index: boolean
|
|
49
|
+
}
|
|
50
|
+
declare type ParsedLine = | { kind: 'columns', columns: Column[], indexes?: { name: string, columns: string[], unique: boolean }[] }
|
|
51
|
+
| { kind: 'index', index: { name: string, columns: string[], unique: boolean } }
|
|
52
|
+
| { kind: 'skip' }
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export declare function parseLaravelModel(source: string): ParsedModel | null;
|
|
2
|
+
/**
|
|
3
|
+
* Laravel Eloquent → Stacks `defineModel({})` translator.
|
|
4
|
+
*
|
|
5
|
+
* Eloquent models are PHP classes that lean on convention plus a few
|
|
6
|
+
* `protected $foo = ...` properties to describe schema and behaviour.
|
|
7
|
+
* We pull the bits we need by regex:
|
|
8
|
+
*
|
|
9
|
+
* - class name → model `name`
|
|
10
|
+
* - protected $table → `table`
|
|
11
|
+
* - protected $fillable → which attributes are `fillable: true`
|
|
12
|
+
* - protected $casts → which attributes get a typed
|
|
13
|
+
* validation rule
|
|
14
|
+
* - hasMany/hasOne/belongsTo → relationship hints in the report
|
|
15
|
+
*
|
|
16
|
+
* Anything else (scopes, accessors, mutators, observers) gets dropped
|
|
17
|
+
* with a note in the migration report. The user can re-add them by
|
|
18
|
+
* hand from the Stacks docs.
|
|
19
|
+
*/
|
|
20
|
+
export declare interface ParsedModel {
|
|
21
|
+
className: string
|
|
22
|
+
table: string
|
|
23
|
+
fillable: string[]
|
|
24
|
+
hidden: string[]
|
|
25
|
+
casts: Record<string, string>
|
|
26
|
+
relationships: ParsedRelationship[]
|
|
27
|
+
tsSource: string
|
|
28
|
+
notes: string[]
|
|
29
|
+
}
|
|
30
|
+
export declare interface ParsedRelationship {
|
|
31
|
+
name: string
|
|
32
|
+
kind: 'belongsTo' | 'hasMany' | 'hasOne' | 'belongsToMany'
|
|
33
|
+
target: string
|
|
34
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export declare interface MigrateProjectRequest {
|
|
2
|
+
source: string
|
|
3
|
+
target: string
|
|
4
|
+
from: SourceFramework
|
|
5
|
+
dryRun?: boolean
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* A single output produced (or attempted) by a driver. Drivers append
|
|
9
|
+
* one `ReportEntry` per source file they touch so the report reads
|
|
10
|
+
* like a checklist of what was translated.
|
|
11
|
+
*/
|
|
12
|
+
export declare interface ReportEntry {
|
|
13
|
+
source: string
|
|
14
|
+
target: string
|
|
15
|
+
status: 'translated' | 'copied' | 'skipped' | 'failed'
|
|
16
|
+
note?: string
|
|
17
|
+
}
|
|
18
|
+
export declare interface MigrationReport {
|
|
19
|
+
source: string
|
|
20
|
+
target: string
|
|
21
|
+
from: SourceFramework
|
|
22
|
+
startedAt: string
|
|
23
|
+
finishedAt: string
|
|
24
|
+
entries: ReportEntry[]
|
|
25
|
+
}
|
|
26
|
+
export declare interface Driver {
|
|
27
|
+
readonly name: SourceFramework
|
|
28
|
+
migrate: (req: MigrateProjectRequest) => Promise<ReportEntry[]>
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Shared types for the `./buddy migrate:project` migrators (Laravel,
|
|
32
|
+
* Rails, …). Each driver consumes a source-project root and emits
|
|
33
|
+
* Stacks-shaped files under a target root, returning a structured
|
|
34
|
+
* report so the CLI can show the user what landed and what got
|
|
35
|
+
* skipped.
|
|
36
|
+
*
|
|
37
|
+
* The migration is intentionally best-effort. The drivers prioritise
|
|
38
|
+
* the high-value 80% (schema → migrations, models, env) and surface
|
|
39
|
+
* unrecognised constructs as `skipped` entries rather than throwing —
|
|
40
|
+
* a partial port the user can clean up beats a full failure mid-run.
|
|
41
|
+
*
|
|
42
|
+
* stacksjs/stacks#1241.
|
|
43
|
+
*/
|
|
44
|
+
export type SourceFramework = 'laravel' | 'rails';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/buddy",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.70.
|
|
4
|
+
"version": "0.70.54",
|
|
5
5
|
"description": "Meet Buddy. The Stacks runtime.",
|
|
6
6
|
"author": "Chris Breuer",
|
|
7
7
|
"contributors": [
|
|
@@ -96,13 +96,14 @@
|
|
|
96
96
|
"@stacksjs/collections": "^0.70.23",
|
|
97
97
|
"@stacksjs/config": "^0.70.23",
|
|
98
98
|
"@stacksjs/database": "^0.70.23",
|
|
99
|
-
"@stacksjs/desktop": "^0.
|
|
99
|
+
"@stacksjs/desktop": "^0.2.82",
|
|
100
100
|
"@stacksjs/dns": "^0.70.23",
|
|
101
101
|
"@stacksjs/email": "^0.70.23",
|
|
102
102
|
"@stacksjs/enums": "^0.70.23",
|
|
103
103
|
"@stacksjs/error-handling": "^0.70.23",
|
|
104
104
|
"@stacksjs/events": "^0.70.23",
|
|
105
105
|
"@stacksjs/git": "^0.70.23",
|
|
106
|
+
"@stacksjs/gitit": "^0.2.5",
|
|
106
107
|
"@stacksjs/health": "^0.70.23",
|
|
107
108
|
"@stacksjs/dnsx": "^0.2.3",
|
|
108
109
|
"@stacksjs/httx": "^0.1.10",
|
|
@@ -115,7 +116,7 @@
|
|
|
115
116
|
"@stacksjs/payments": "^0.70.23",
|
|
116
117
|
"@stacksjs/realtime": "^0.70.23",
|
|
117
118
|
"@stacksjs/router": "^0.70.23",
|
|
118
|
-
"@stacksjs/rpx": "^0.11.
|
|
119
|
+
"@stacksjs/rpx": "^0.11.13",
|
|
119
120
|
"@stacksjs/search-engine": "^0.70.23",
|
|
120
121
|
"@stacksjs/security": "^0.70.23",
|
|
121
122
|
"@stacksjs/server": "^0.70.23",
|
|
@@ -127,7 +128,7 @@
|
|
|
127
128
|
"@stacksjs/ui": "^0.70.23",
|
|
128
129
|
"@stacksjs/utils": "^0.70.23",
|
|
129
130
|
"@stacksjs/validation": "^0.70.23",
|
|
130
|
-
"@stacksjs/ts-cloud": "^0.
|
|
131
|
+
"@stacksjs/ts-cloud": "^0.7.12"
|
|
131
132
|
},
|
|
132
133
|
"devDependencies": {
|
|
133
134
|
"better-dx": "^0.2.12"
|
package/dist/package.json
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|