@stacksjs/features 0.74.4

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 ADDED
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright (c) 2023 Open Web Foundation
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # @stacksjs/features
2
+
3
+ Which files and tables belong to which optional Stacks feature.
4
+
5
+ Two very different callers need this manifest and neither should own it. The
6
+ CLI installs and uninstalls a feature, so it needs the file list.
7
+ The migration runner hides a disabled feature's migrations before a run, so it
8
+ needs the table list.
9
+
10
+ The manifest used to live in `@stacksjs/buddy`, and the migration runner
11
+ reached into the CLI through a best-effort dynamic import to read it — so
12
+ `@stacksjs/database` depended on `@stacksjs/buddy`, and the two sat inside a
13
+ dependency cycle that no publish order could satisfy.
14
+
15
+ ```ts
16
+ import { FEATURE_NAMES, migrationFeature, migrationTable } from '@stacksjs/features'
17
+
18
+ migrationTable('0000000133-create-campaigns-table.sql') // 'campaigns'
19
+ migrationFeature('0000000133-create-campaigns-table.sql') // 'marketing'
20
+ ```
21
+
22
+ Nothing here reads config or touches a database — it is the manifest and three
23
+ lookups over it. Deciding whether a feature is *enabled* is `@stacksjs/config`'s
24
+ job, and acting on that is the caller's.
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Parse a migration filename like `0000000045-create-posts-table.sql` or
3
+ * `0000000085-alter-posts-author_id.sql` to extract the table name it
4
+ * acts on. Returns `null` for filenames that don't match the
5
+ * recognised `create-<table>-table` / `alter-<table>-` shapes — those
6
+ * pass through the gate unchanged.
7
+ *
8
+ * Index migrations (`create-<table>_<col>_unique-index-in-<table>.sql`)
9
+ * use the trailing `-in-<table>.sql` segment as the source of truth
10
+ * since the leading segment includes the index name. The other
11
+ * forms read the table from the segment immediately after the verb.
12
+ */
13
+ export declare function migrationTable(filename: string): string | null;
14
+ /**
15
+ * Returns the feature that owns the given migration filename, or `null`
16
+ * if the migration isn't claimed by any feature (in which case it
17
+ * always runs). Used by the migration runner's gating pass.
18
+ */
19
+ export declare function migrationFeature(filename: string): FeatureName | null;
20
+ /**
21
+ * True when an application-owned, top-level model explicitly declares a table.
22
+ * This lets apps intentionally use generic names such as `payments` without
23
+ * their migrations being mistaken for disabled framework-feature scaffolding.
24
+ * Root models listed in FEATURE_FILES remain feature-owned and do not override
25
+ * the gate.
26
+ */
27
+ export declare function appModelClaimsTable(table: string, root?: string): boolean;
28
+ export declare const FEATURE_NAMES: readonly ['dashboard', 'commerce', 'cms', 'forms', 'marketing', 'monitoring', 'realtime', 'queue'];
29
+ /**
30
+ * Per-feature stamped file/directory manifest. Paths are relative to the
31
+ * project root and mirror the layout that `./buddy new` lays down. Entries
32
+ * ending in `/` are directory trees (recursive remove on uninstall); bare
33
+ * paths are single files.
34
+ *
35
+ * A feature must also claim the shared files its own actions IMPORT.
36
+ * `app/Actions/Dashboard/dashboard-response.ts` is the case that bit: five
37
+ * features publish a subdirectory of `app/Actions/Dashboard/`, and the actions
38
+ * in each import `../dashboard-response`. Without it, `<feature>:install`
39
+ * copied 22 actions whose very first import did not resolve.
40
+ *
41
+ * Manifests intentionally overlap where features share scaffolding —
42
+ * `dashboard` claims the umbrella `app/Actions/Dashboard/` even though
43
+ * `app/Actions/Dashboard/Content/` is also claimed by `cms`. Both the
44
+ * uninstall delete and the doctor orphan check are idempotent
45
+ * (already-gone paths are skipped silently), so the overlap is safe.
46
+ *
47
+ * Adding a new file to one of these directories does **not** require a
48
+ * manifest update — directory entries are recursive. Only add an entry
49
+ * when a feature introduces a new top-level path the framework didn't
50
+ * already claim.
51
+ * @defaultValue
52
+ * ```ts
53
+ * {
54
+ * forms: [ 'app/Models/Forms/', ],
55
+ * cms: [ 'app/Actions/Cms/', 'app/Actions/Dashboard/Content/', 'app/Actions/Dashboard/dashboard-response.ts', 'app/Models/Content/', 'app/Models/Tag.ts', 'app/Models/Comment.ts', 'resources/views/dashboard/content/', ],
56
+ * commerce: [ 'app/Actions/Commerce/', 'app/Actions/Dashboard/Commerce/', 'app/Actions/Dashboard/dashboard-response.ts', 'app/Models/commerce/', 'resources/components/Dashboard/Commerce/', 'resources/views/dashboard/commerce/', ],
57
+ * dashboard: [ 'app/Actions/Dashboard/', 'resources/components/Dashboard/', 'resources/views/dashboard/', 'routes/dashboard.ts', 'routes/dashboard-api.ts', ],
58
+ * marketing: [ 'app/Actions/Dashboard/Marketing/', 'app/Actions/Dashboard/dashboard-response.ts', '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/', ],
59
+ * monitoring: [ 'app/Actions/Monitoring/', 'app/Actions/TestErrorAction.ts', 'app/Models/Error.ts', 'functions/monitoring/', 'resources/views/dashboard/monitoring/', 'resources/views/dashboard/errors/', ],
60
+ * realtime: [ 'app/Actions/Realtime/', 'app/Actions/Dashboard/Realtime/', 'app/Actions/Dashboard/dashboard-response.ts', 'app/Models/realtime/', 'app/Broadcasts/', 'functions/realtime/', 'resources/views/dashboard/realtime/', ],
61
+ * queue: [ 'app/Actions/Queue/', 'app/Actions/Dashboard/Jobs/', 'app/Actions/Dashboard/dashboard-response.ts', 'app/Jobs/', 'app/Models/Job.ts', 'app/Models/FailedJob.ts', 'functions/jobs.ts', 'resources/views/dashboard/queue/', 'resources/views/dashboard/jobs/', ]
62
+ * }
63
+ * ```
64
+ */
65
+ export declare const FEATURE_FILES: Record<FeatureName, readonly string[]>;
66
+ /**
67
+ * Per-feature database table ownership (stacksjs/stacks#1854).
68
+ *
69
+ * Stacks generates SQL migrations from model files, so each feature's
70
+ * tables map 1:1 with the models in its `FEATURE_FILES.app/Models/...`
71
+ * entries. Listed here explicitly rather than derived at runtime so
72
+ * additions are visible in a single grep-able place and the migration
73
+ * gate doesn't depend on filesystem scanning at boot.
74
+ *
75
+ * The migration runner consults this when `config.<feature>.enabled =
76
+ * false`: matching `*-create-<table>-table.sql` (and `*-alter-<table>-*.sql`)
77
+ * files get hidden for the duration of the run, so a project that
78
+ * never installed CMS doesn't materialize `posts`, `pages`,
79
+ * `comments`, etc. on `./buddy migrate`.
80
+ *
81
+ * Tables on this list are scoped to a single feature. Tables shared
82
+ * across features (none today, but `categories` could end up here)
83
+ * should stay out of the manifest until that's resolved — the runner
84
+ * defaults to "run unless owned by a disabled feature".
85
+ * @defaultValue
86
+ * ```ts
87
+ * {
88
+ * forms: ['forms', 'form_fields', 'form_submissions'],
89
+ * cms: [ 'posts', 'pages', 'comments', 'tags', 'authors', 'categories', 'taggable_models', 'categorizable_models', 'commentables', 'page_revisions', 'redirects', 'menus', 'menu_items', ],
90
+ * commerce: [ 'products', 'product_variants', 'product_units', 'manufacturers', 'orders', 'order_items', 'order_idempotency', 'carts', 'cart_items', 'payments', 'payment_methods', 'payment_products', 'payment_transactions', 'customers', 'subscribers', 'subscriber_emails', 'subscriptions', 'gift_cards', 'coupons', 'transactions', 'reviews', 'couriers', 'courier_pings', 'delivery_routes', 'delivery_stops', 'digital_deliveries', 'shipping_methods', 'shipping_rates', 'shipping_zones', 'license_keys', 'loyalty_points', 'loyalty_rewards', 'print_devices', 'receipts', 'tax_rates', 'waitlist_products', 'waitlist_restaurants', 'auctions', 'auction_items', 'bids', 'pledges', ],
91
+ * dashboard: [ 'boards', 'board_columns', 'cards', 'card_labels', 'card_assignees', 'card_comments', 'labels', 'ci_run_states', 'ci_runner_samples', 'ci_runner_alert_states', 'requests', 'logs', ],
92
+ * marketing: [ 'campaigns', 'campaign_sends', 'email_lists', 'email_list_subscribers', 'social_posts', 'mail_preferences', ],
93
+ * monitoring: ['errors'],
94
+ * realtime: ['websockets'],
95
+ * queue: ['jobs', 'failed_jobs']
96
+ * }
97
+ * ```
98
+ */
99
+ export declare const FEATURE_TABLES: Record<FeatureName, readonly string[]>;
100
+ export type FeatureName = (typeof FEATURE_NAMES)[number];
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{existsSync as p,readdirSync as d,readFileSync as m}from"fs";import{join as t}from"path";import{projectPath as l}from"@stacksjs/path";var i=["dashboard","commerce","cms","forms","marketing","monitoring","realtime","queue"],u={forms:["app/Models/Forms/"],cms:["app/Actions/Cms/","app/Actions/Dashboard/Content/","app/Actions/Dashboard/dashboard-response.ts","app/Models/Content/","app/Models/Tag.ts","app/Models/Comment.ts","resources/views/dashboard/content/"],commerce:["app/Actions/Commerce/","app/Actions/Dashboard/Commerce/","app/Actions/Dashboard/dashboard-response.ts","app/Models/commerce/","resources/components/Dashboard/Commerce/","resources/views/dashboard/commerce/"],dashboard:["app/Actions/Dashboard/","resources/components/Dashboard/","resources/views/dashboard/","routes/dashboard.ts","routes/dashboard-api.ts"],marketing:["app/Actions/Dashboard/Marketing/","app/Actions/Dashboard/dashboard-response.ts","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/"],monitoring:["app/Actions/Monitoring/","app/Actions/TestErrorAction.ts","app/Models/Error.ts","functions/monitoring/","resources/views/dashboard/monitoring/","resources/views/dashboard/errors/"],realtime:["app/Actions/Realtime/","app/Actions/Dashboard/Realtime/","app/Actions/Dashboard/dashboard-response.ts","app/Models/realtime/","app/Broadcasts/","functions/realtime/","resources/views/dashboard/realtime/"],queue:["app/Actions/Queue/","app/Actions/Dashboard/Jobs/","app/Actions/Dashboard/dashboard-response.ts","app/Jobs/","app/Models/Job.ts","app/Models/FailedJob.ts","functions/jobs.ts","resources/views/dashboard/queue/","resources/views/dashboard/jobs/"]},b={forms:["forms","form_fields","form_submissions"],cms:["posts","pages","comments","tags","authors","categories","taggable_models","categorizable_models","commentables","page_revisions","redirects","menus","menu_items"],commerce:["products","product_variants","product_units","manufacturers","orders","order_items","order_idempotency","carts","cart_items","payments","payment_methods","payment_products","payment_transactions","customers","subscribers","subscriber_emails","subscriptions","gift_cards","coupons","transactions","reviews","couriers","courier_pings","delivery_routes","delivery_stops","digital_deliveries","shipping_methods","shipping_rates","shipping_zones","license_keys","loyalty_points","loyalty_rewards","print_devices","receipts","tax_rates","waitlist_products","waitlist_restaurants","auctions","auction_items","bids","pledges"],dashboard:["boards","board_columns","cards","card_labels","card_assignees","card_comments","labels","ci_run_states","ci_runner_samples","ci_runner_alert_states","requests","logs"],marketing:["campaigns","campaign_sends","email_lists","email_list_subscribers","social_posts","mail_preferences"],monitoring:["errors"],realtime:["websockets"],queue:["jobs","failed_jobs"]};function _(r){let o=r.match(/-in-([a-z0-9_]+)\.sql$/i);if(o)return o[1]??null;let s=r.match(/-create-([a-z0-9_]+)-table\.sql$/i);if(s)return s[1]??null;let a=r.match(/-alter-([a-z0-9_]+)-/i);if(a)return a[1]??null;return null}function M(r){let o=_(r);if(!o)return null;for(let s of i)if(b[s].includes(o))return s;return null}function A(r,o=l()){let s=t(o,"app/Models");if(!p(s))return!1;let a=new Set(i.flatMap((e)=>u[e]).filter((e)=>e.startsWith("app/Models/")&&!e.endsWith("/"))),n=r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),c=new RegExp(`\\btable\\s*:\\s*['"]${n}['"]`);for(let e of d(s,{withFileTypes:!0})){if(!e.isFile()||!/\.[cm]?[jt]s$/.test(e.name))continue;if(a.has(`app/Models/${e.name}`))continue;if(c.test(m(t(s,e.name),"utf8")))return!0}return!1}export{u as FEATURE_FILES,i as FEATURE_NAMES,b as FEATURE_TABLES,A as appModelClaimsTable,M as migrationFeature,_ as migrationTable};
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@stacksjs/features",
3
+ "type": "module",
4
+ "version": "0.74.4",
5
+ "description": "Which files and tables belong to which optional Stacks feature.",
6
+ "author": "Chris Breuer",
7
+ "contributors": [
8
+ "Chris Breuer <chris@stacksjs.com>"
9
+ ],
10
+ "license": "MIT",
11
+ "funding": "https://github.com/sponsors/chrisbbreuer",
12
+ "homepage": "https://github.com/stacksjs/stacks/tree/main/storage/framework/core/features#readme",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/stacksjs/stacks.git",
16
+ "directory": "./storage/framework/core/features"
17
+ },
18
+ "bugs": {
19
+ "url": "https://github.com/stacksjs/stacks/issues"
20
+ },
21
+ "keywords": [
22
+ "features",
23
+ "migrations",
24
+ "manifest",
25
+ "stacks"
26
+ ],
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "bun": "./dist/index.js",
31
+ "import": "./dist/index.js"
32
+ },
33
+ "./*": {
34
+ "import": "./dist/*"
35
+ }
36
+ },
37
+ "module": "./dist/index.js",
38
+ "types": "./dist/index.d.ts",
39
+ "files": [
40
+ "README.md",
41
+ "dist"
42
+ ],
43
+ "scripts": {
44
+ "build": "bun build.ts",
45
+ "typecheck": "bun --bun tsc --noEmit"
46
+ },
47
+ "dependencies": {
48
+ "@stacksjs/path": "0.74.4"
49
+ }
50
+ }