@weft-js/create 0.1.0 → 0.1.2

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.
Files changed (127) hide show
  1. package/README.md +60 -10
  2. package/bin/create-weft.mjs +62 -14
  3. package/package.json +26 -16
  4. package/scripts/tarballs.mjs +56 -6
  5. package/src/data.mjs +90 -18
  6. package/src/index.mjs +210 -51
  7. package/src/stack/pages.mjs +151 -0
  8. package/src/stack/schema.mjs +195 -0
  9. package/src/stack.mjs +102 -313
  10. package/src/templates.mjs +174 -0
  11. package/src/wizard.mjs +65 -16
  12. package/templates/admin/files/drizzle/.schema-hash +1 -0
  13. package/templates/admin/files/drizzle/0000_admin.sql +136 -0
  14. package/templates/admin/files/drizzle/meta/0000_snapshot.json +771 -0
  15. package/templates/admin/files/drizzle/meta/_journal.json +13 -0
  16. package/templates/admin/files/public/favicon.svg +1 -0
  17. package/templates/admin/files/src/auth.server.ts +24 -0
  18. package/templates/admin/files/src/components/AuthPage.tsx +33 -0
  19. package/templates/admin/files/src/components/Failed.tsx +19 -0
  20. package/templates/admin/files/src/components/Logo.tsx +16 -0
  21. package/templates/admin/files/src/components/OrderDetail.tsx +93 -0
  22. package/templates/admin/files/src/components/OrdersFilters.tsx +44 -0
  23. package/templates/admin/files/src/components/OrdersTable.tsx +144 -0
  24. package/templates/admin/files/src/components/Overview.tsx +113 -0
  25. package/templates/admin/files/src/components/StatusBadge.tsx +12 -0
  26. package/templates/admin/files/src/db/schema.ts +68 -0
  27. package/templates/admin/files/src/lib/orders.ts +60 -0
  28. package/templates/admin/files/src/routes/404.tsx +20 -0
  29. package/templates/admin/files/src/routes/_error.tsx +20 -0
  30. package/templates/admin/files/src/routes/_layout.tsx +6 -0
  31. package/templates/admin/files/src/routes/admin/_layout.tsx +89 -0
  32. package/templates/admin/files/src/routes/admin/index.tsx +45 -0
  33. package/templates/admin/files/src/routes/admin/nested/_layout.tsx +35 -0
  34. package/templates/admin/files/src/routes/admin/nested/index.tsx +77 -0
  35. package/templates/admin/files/src/routes/admin/orders/[id].tsx +24 -0
  36. package/templates/admin/files/src/routes/admin/orders/index.tsx +28 -0
  37. package/templates/admin/files/src/routes/admin/per-row.tsx +81 -0
  38. package/templates/admin/files/src/routes/admin/slow.tsx +74 -0
  39. package/templates/admin/files/src/routes/index.tsx +13 -0
  40. package/templates/admin/files/src/routes/login.tsx +53 -0
  41. package/templates/admin/files/src/routes/signup.tsx +60 -0
  42. package/templates/admin/files/src/server/orders.ts +193 -0
  43. package/templates/admin/files/src/server/reports.ts +92 -0
  44. package/templates/admin/files/src/server/session.ts +42 -0
  45. package/templates/admin/files/src/theme.css +43 -0
  46. package/templates/admin/files/weft.config.ts +31 -0
  47. package/templates/admin/template.json +22 -0
  48. package/templates/catalog/files/drizzle/.schema-hash +1 -0
  49. package/templates/catalog/files/drizzle/0000_catalog.sql +47 -0
  50. package/templates/catalog/files/drizzle/meta/0000_snapshot.json +263 -0
  51. package/templates/catalog/files/drizzle/meta/_journal.json +13 -0
  52. package/templates/catalog/files/public/favicon.svg +1 -0
  53. package/templates/catalog/files/seed.sql +13 -0
  54. package/templates/catalog/files/src/components/AddToCart.tsx +22 -0
  55. package/templates/catalog/files/src/components/Aisles.tsx +31 -0
  56. package/templates/catalog/files/src/components/CartLines.tsx +96 -0
  57. package/templates/catalog/files/src/components/CartLink.tsx +29 -0
  58. package/templates/catalog/files/src/components/Failed.tsx +19 -0
  59. package/templates/catalog/files/src/components/Featured.tsx +19 -0
  60. package/templates/catalog/files/src/components/Filters.tsx +40 -0
  61. package/templates/catalog/files/src/components/Logo.tsx +16 -0
  62. package/templates/catalog/files/src/components/ProductArt.tsx +42 -0
  63. package/templates/catalog/files/src/components/ProductCard.tsx +42 -0
  64. package/templates/catalog/files/src/components/ProductList.tsx +65 -0
  65. package/templates/catalog/files/src/components/Related.tsx +19 -0
  66. package/templates/catalog/files/src/components/SiteFooter.tsx +9 -0
  67. package/templates/catalog/files/src/components/SiteHeader.tsx +16 -0
  68. package/templates/catalog/files/src/db/schema.ts +53 -0
  69. package/templates/catalog/files/src/lib/catalog.ts +35 -0
  70. package/templates/catalog/files/src/routes/404.tsx +18 -0
  71. package/templates/catalog/files/src/routes/_error.tsx +18 -0
  72. package/templates/catalog/files/src/routes/_layout.tsx +14 -0
  73. package/templates/catalog/files/src/routes/c/[slug].tsx +53 -0
  74. package/templates/catalog/files/src/routes/cart.tsx +29 -0
  75. package/templates/catalog/files/src/routes/index.tsx +84 -0
  76. package/templates/catalog/files/src/routes/p/[slug].tsx +67 -0
  77. package/templates/catalog/files/src/server/cart.ts +125 -0
  78. package/templates/catalog/files/src/server/catalog.ts +107 -0
  79. package/templates/catalog/files/src/theme.css +159 -0
  80. package/templates/catalog/files/weft.config.ts +30 -0
  81. package/templates/catalog/template.json +20 -0
  82. package/templates/landing/files/.weft/plugins.lock.json +42 -0
  83. package/templates/landing/files/assets/fonts/LICENSE-figtree.txt +93 -0
  84. package/templates/landing/files/assets/fonts/figtree.woff2 +0 -0
  85. package/templates/landing/files/assets/hero.webp +0 -0
  86. package/templates/landing/files/drizzle/.schema-hash +1 -0
  87. package/templates/landing/files/drizzle/0000_landing.sql +6 -0
  88. package/templates/landing/files/drizzle/meta/0000_snapshot.json +60 -0
  89. package/templates/landing/files/drizzle/meta/_journal.json +13 -0
  90. package/templates/landing/files/public/favicon.svg +1 -0
  91. package/templates/landing/files/src/components/SiteFooter.tsx +24 -0
  92. package/templates/landing/files/src/components/SiteHeader.tsx +23 -0
  93. package/templates/landing/files/src/components/WaitlistForm.tsx +36 -0
  94. package/templates/landing/files/src/db/schema.ts +7 -0
  95. package/templates/landing/files/src/routes/404.tsx +24 -0
  96. package/templates/landing/files/src/routes/_error.tsx +24 -0
  97. package/templates/landing/files/src/routes/_layout.tsx +14 -0
  98. package/templates/landing/files/src/routes/index.tsx +130 -0
  99. package/templates/landing/files/src/routes/joined.tsx +30 -0
  100. package/templates/landing/files/src/routes/pricing.tsx +131 -0
  101. package/templates/landing/files/src/routes/privacy.tsx +56 -0
  102. package/templates/landing/files/src/server/waitlist.ts +13 -0
  103. package/templates/landing/files/src/theme.css +108 -0
  104. package/templates/landing/files/weft.config.ts +44 -0
  105. package/templates/landing/template.json +17 -0
  106. package/templates/todo/files/drizzle/.schema-hash +1 -0
  107. package/templates/todo/files/drizzle/0000_todo.sql +82 -0
  108. package/templates/todo/files/drizzle/meta/0000_snapshot.json +462 -0
  109. package/templates/todo/files/drizzle/meta/_journal.json +13 -0
  110. package/templates/todo/files/public/favicon.svg +1 -0
  111. package/templates/todo/files/src/components/AuthPage.tsx +33 -0
  112. package/templates/todo/files/src/components/Logo.tsx +14 -0
  113. package/templates/todo/files/src/components/NewTodo.tsx +27 -0
  114. package/templates/todo/files/src/components/TodoList.tsx +101 -0
  115. package/templates/todo/files/src/db/schema.ts +18 -0
  116. package/templates/todo/files/src/lib/views.ts +13 -0
  117. package/templates/todo/files/src/routes/404.tsx +20 -0
  118. package/templates/todo/files/src/routes/_error.tsx +20 -0
  119. package/templates/todo/files/src/routes/_layout.tsx +6 -0
  120. package/templates/todo/files/src/routes/index.tsx +13 -0
  121. package/templates/todo/files/src/routes/login.tsx +53 -0
  122. package/templates/todo/files/src/routes/signup.tsx +57 -0
  123. package/templates/todo/files/src/routes/todos.tsx +87 -0
  124. package/templates/todo/files/src/server/todos.ts +98 -0
  125. package/templates/todo/files/src/theme.css +71 -0
  126. package/templates/todo/files/weft.config.ts +30 -0
  127. package/templates/todo/template.json +18 -0
package/src/wizard.mjs CHANGED
@@ -1,47 +1,93 @@
1
- // The create-weft wizard (research 34 §3): each choice is a list with the default selected. A flag answers its question,
1
+ // The create-weft wizard: each choice is a list with the default selected. A flag answers its question,
2
2
  // and with no terminal or with --yes every unanswered question takes its default, so agents and CI never see a prompt.
3
3
  import { emitKeypressEvents } from 'node:readline'
4
4
  import { CONFIGS, DB, LAYERS, RUNTIMES, UI } from './index.mjs'
5
5
 
6
6
  const PMS = ['npm', 'pnpm', 'bun', 'deno', 'yarn']
7
7
  const LABELS = {
8
- warp: 'warp', headless: 'Headless (warp behaviour, your markup)', tailwind: 'Tailwind', none: 'None',
9
- sqlite: 'SQLite', postgres: 'Postgres', mysql: 'MySQL', drizzle: 'Drizzle', prisma: 'Prisma', native: 'Native (no ORM)', node: 'Node', bun: 'Bun', deno: 'Deno', npm: 'npm', pnpm: 'pnpm', yarn: 'Yarn',
10
- ts: 'TypeScript', json: 'JSON', yaml: 'YAML',
8
+ warp: 'warp',
9
+ headless: 'Headless (warp behaviour, your markup)',
10
+ tailwind: 'Tailwind',
11
+ none: 'None',
12
+ sqlite: 'SQLite',
13
+ postgres: 'Postgres',
14
+ mysql: 'MySQL',
15
+ drizzle: 'Drizzle',
16
+ prisma: 'Prisma',
17
+ native: 'Native (no ORM)',
18
+ node: 'Node',
19
+ bun: 'Bun',
20
+ deno: 'Deno',
21
+ npm: 'npm',
22
+ pnpm: 'pnpm',
23
+ yarn: 'Yarn',
24
+ ts: 'TypeScript',
25
+ json: 'JSON',
26
+ yaml: 'YAML',
11
27
  }
12
- const list = values => values.map(value => ({ value, label: LABELS[value] ?? value }))
13
- const yesNo = [{ value: true, label: 'Yes' }, { value: false, label: 'No' }]
28
+ const list = (values) => values.map((value) => ({ value, label: LABELS[value] ?? value }))
29
+ const yesNo = [
30
+ { value: true, label: 'Yes' },
31
+ { value: false, label: 'No' },
32
+ ]
14
33
  /** Whichever of Node, Bun and Deno is running this: a machine may have only that one. */
15
34
  const host = process.versions.deno ? 'deno' : process.versions.bun ? 'bun' : 'node'
16
- const packageManager = env => (host === 'node' ? PMS.find(pm => env.npm_config_user_agent?.startsWith(pm)) ?? 'npm' : host)
35
+ const packageManager = (env) =>
36
+ host === 'node' ? (PMS.find((pm) => env.npm_config_user_agent?.startsWith(pm)) ?? 'npm') : host
17
37
 
18
38
  /** In order. `when` skips a question that doesn't apply. `sideEffect` questions default to off with no terminal. */
19
- export const questions = env => [
39
+ export const questions = (env) => [
20
40
  { key: 'ui', label: 'UI', default: 'warp', options: list(UI) },
21
41
  { key: 'db', label: 'Database', default: 'sqlite', options: list(DB) },
22
- { key: 'layer', label: 'Data layer', default: 'drizzle', skip: 'drizzle', options: list(LAYERS), when: a => a.db !== 'none' },
23
- { key: 'auth', label: 'Sign-in', default: true, options: [{ value: true, label: 'Better Auth' }, { value: 'custom', label: 'Bring your own' }, { value: false, label: 'None' }], when: a => a.db !== 'none' },
42
+ {
43
+ key: 'layer',
44
+ label: 'Data layer',
45
+ default: 'drizzle',
46
+ skip: 'drizzle',
47
+ options: list(LAYERS),
48
+ when: (a) => a.db !== 'none',
49
+ },
50
+ {
51
+ key: 'auth',
52
+ label: 'Sign-in',
53
+ default: true,
54
+ options: [
55
+ { value: true, label: 'Better Auth' },
56
+ { value: 'custom', label: 'Bring your own' },
57
+ { value: false, label: 'None' },
58
+ ],
59
+ when: (a) => a.db !== 'none',
60
+ },
24
61
  { key: 'runtime', label: 'Runtime', default: host, options: list(RUNTIMES) },
25
62
  { key: 'config', label: 'Config file', default: 'ts', options: list(CONFIGS) },
26
63
  { key: 'pm', label: 'Package manager', default: packageManager(env), options: list(PMS) },
27
64
  { key: 'git', label: 'Start a git repository', default: true, options: yesNo, sideEffect: true },
28
- { key: 'install', label: 'Install dependencies', default: true, options: yesNo, sideEffect: true },
65
+ {
66
+ key: 'install',
67
+ label: 'Install dependencies',
68
+ default: true,
69
+ options: yesNo,
70
+ sideEffect: true,
71
+ },
29
72
  ]
30
73
 
31
74
  /** One list on a terminal: arrows move, Enter picks. Resolves with the option's value. */
32
75
  export function select({ label, options, default: initial }, { input, output }) {
33
- let at = Math.max(0, options.findIndex(o => o.value === initial))
34
- const draw = first => {
76
+ let at = Math.max(
77
+ 0,
78
+ options.findIndex((o) => o.value === initial),
79
+ )
80
+ const draw = (first) => {
35
81
  if (!first) output.write(`\x1b[${options.length}A`)
36
82
  options.forEach((o, i) => output.write(`\x1b[2K${i === at ? '› ' : ' '}${o.label}\n`))
37
83
  }
38
- return new Promise(ok => {
84
+ return new Promise((ok) => {
39
85
  output.write(`${label}\n`)
40
86
  draw(true)
41
87
  emitKeypressEvents(input)
42
88
  input.setRawMode?.(true)
43
89
  input.resume()
44
- const done = value => {
90
+ const done = (value) => {
45
91
  input.off('keypress', onKey)
46
92
  input.setRawMode?.(false)
47
93
  input.pause()
@@ -63,7 +109,10 @@ export function select({ label, options, default: initial }, { input, output })
63
109
  }
64
110
 
65
111
  /** Every choice, from flags, then the terminal if there is one, then the defaults. */
66
- export async function resolveChoices(given, { interactive, yes = false, env = process.env, io } = {}) {
112
+ export async function resolveChoices(
113
+ given,
114
+ { interactive, yes = false, env = process.env, io } = {},
115
+ ) {
67
116
  const choices = { ...given }
68
117
  for (const q of questions(env)) {
69
118
  if (q.when && !q.when(choices)) {
@@ -0,0 +1 @@
1
+ 4fc125bb55cd53a8
@@ -0,0 +1,136 @@
1
+ CREATE TABLE IF NOT EXISTS "categories" (
2
+ "id" serial PRIMARY KEY NOT NULL,
3
+ "slug" text NOT NULL,
4
+ "name" text NOT NULL,
5
+ CONSTRAINT "categories_slug_unique" UNIQUE("slug")
6
+ );
7
+ --> statement-breakpoint
8
+ CREATE TABLE IF NOT EXISTS "customers" (
9
+ "id" serial PRIMARY KEY NOT NULL,
10
+ "name" text NOT NULL,
11
+ "email" text NOT NULL,
12
+ "city" text NOT NULL,
13
+ CONSTRAINT "customers_email_unique" UNIQUE("email")
14
+ );
15
+ --> statement-breakpoint
16
+ CREATE TABLE IF NOT EXISTS "order_items" (
17
+ "id" serial PRIMARY KEY NOT NULL,
18
+ "order_id" integer NOT NULL,
19
+ "product_id" integer NOT NULL,
20
+ "quantity" integer NOT NULL,
21
+ "price_cents" integer NOT NULL
22
+ );
23
+ --> statement-breakpoint
24
+ CREATE TABLE IF NOT EXISTS "orders" (
25
+ "id" serial PRIMARY KEY NOT NULL,
26
+ "customer_id" integer NOT NULL,
27
+ "status" text NOT NULL,
28
+ "total_cents" integer NOT NULL,
29
+ "created_at" timestamp with time zone NOT NULL
30
+ );
31
+ --> statement-breakpoint
32
+ CREATE TABLE IF NOT EXISTS "products" (
33
+ "id" serial PRIMARY KEY NOT NULL,
34
+ "slug" text NOT NULL,
35
+ "name" text NOT NULL,
36
+ "description" text NOT NULL,
37
+ "category_id" integer NOT NULL,
38
+ "price_cents" integer NOT NULL,
39
+ "stock" integer NOT NULL,
40
+ "featured" boolean DEFAULT false NOT NULL,
41
+ CONSTRAINT "products_slug_unique" UNIQUE("slug")
42
+ );
43
+ --> statement-breakpoint
44
+ CREATE TABLE IF NOT EXISTS "account" (
45
+ "id" text PRIMARY KEY NOT NULL,
46
+ "accountId" text NOT NULL,
47
+ "providerId" text NOT NULL,
48
+ "userId" text NOT NULL,
49
+ "accessToken" text,
50
+ "refreshToken" text,
51
+ "idToken" text,
52
+ "accessTokenExpiresAt" timestamp with time zone,
53
+ "refreshTokenExpiresAt" timestamp with time zone,
54
+ "scope" text,
55
+ "password" text,
56
+ "createdAt" timestamp with time zone NOT NULL,
57
+ "updatedAt" timestamp with time zone NOT NULL
58
+ );
59
+ --> statement-breakpoint
60
+ CREATE TABLE IF NOT EXISTS "jwks" (
61
+ "id" text PRIMARY KEY NOT NULL,
62
+ "publicKey" text NOT NULL,
63
+ "privateKey" text NOT NULL,
64
+ "createdAt" timestamp with time zone NOT NULL,
65
+ "expiresAt" timestamp with time zone,
66
+ "alg" text,
67
+ "crv" text
68
+ );
69
+ --> statement-breakpoint
70
+ CREATE TABLE IF NOT EXISTS "session" (
71
+ "id" text PRIMARY KEY NOT NULL,
72
+ "expiresAt" timestamp with time zone NOT NULL,
73
+ "token" text NOT NULL,
74
+ "createdAt" timestamp with time zone NOT NULL,
75
+ "updatedAt" timestamp with time zone NOT NULL,
76
+ "ipAddress" text,
77
+ "userAgent" text,
78
+ "userId" text NOT NULL,
79
+ CONSTRAINT "session_token_unique" UNIQUE("token")
80
+ );
81
+ --> statement-breakpoint
82
+ CREATE TABLE IF NOT EXISTS "user" (
83
+ "id" text PRIMARY KEY NOT NULL,
84
+ "name" text NOT NULL,
85
+ "email" text NOT NULL,
86
+ "emailVerified" boolean NOT NULL,
87
+ "image" text,
88
+ "createdAt" timestamp with time zone NOT NULL,
89
+ "updatedAt" timestamp with time zone NOT NULL,
90
+ CONSTRAINT "user_email_unique" UNIQUE("email")
91
+ );
92
+ --> statement-breakpoint
93
+ CREATE TABLE IF NOT EXISTS "verification" (
94
+ "id" text PRIMARY KEY NOT NULL,
95
+ "identifier" text NOT NULL,
96
+ "value" text NOT NULL,
97
+ "expiresAt" timestamp with time zone NOT NULL,
98
+ "createdAt" timestamp with time zone,
99
+ "updatedAt" timestamp with time zone
100
+ );
101
+ --> statement-breakpoint
102
+ DO $$ BEGIN
103
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint c JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = c.conkey[1] WHERE c.conrelid = '"order_items"'::regclass AND c.contype = 'f' AND a.attname = 'order_id') THEN
104
+ ALTER TABLE "order_items" ADD CONSTRAINT "order_items_order_id_orders_id_fk" FOREIGN KEY ("order_id") REFERENCES "public"."orders"("id") ON DELETE cascade ON UPDATE no action;
105
+ END IF;
106
+ END $$;--> statement-breakpoint
107
+ DO $$ BEGIN
108
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint c JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = c.conkey[1] WHERE c.conrelid = '"order_items"'::regclass AND c.contype = 'f' AND a.attname = 'product_id') THEN
109
+ ALTER TABLE "order_items" ADD CONSTRAINT "order_items_product_id_products_id_fk" FOREIGN KEY ("product_id") REFERENCES "public"."products"("id") ON DELETE no action ON UPDATE no action;
110
+ END IF;
111
+ END $$;--> statement-breakpoint
112
+ DO $$ BEGIN
113
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint c JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = c.conkey[1] WHERE c.conrelid = '"orders"'::regclass AND c.contype = 'f' AND a.attname = 'customer_id') THEN
114
+ ALTER TABLE "orders" ADD CONSTRAINT "orders_customer_id_customers_id_fk" FOREIGN KEY ("customer_id") REFERENCES "public"."customers"("id") ON DELETE no action ON UPDATE no action;
115
+ END IF;
116
+ END $$;--> statement-breakpoint
117
+ DO $$ BEGIN
118
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint c JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = c.conkey[1] WHERE c.conrelid = '"products"'::regclass AND c.contype = 'f' AND a.attname = 'category_id') THEN
119
+ ALTER TABLE "products" ADD CONSTRAINT "products_category_id_categories_id_fk" FOREIGN KEY ("category_id") REFERENCES "public"."categories"("id") ON DELETE no action ON UPDATE no action;
120
+ END IF;
121
+ END $$;--> statement-breakpoint
122
+ DO $$ BEGIN
123
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint c JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = c.conkey[1] WHERE c.conrelid = '"account"'::regclass AND c.contype = 'f' AND a.attname = 'userId') THEN
124
+ ALTER TABLE "account" ADD CONSTRAINT "account_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
125
+ END IF;
126
+ END $$;--> statement-breakpoint
127
+ DO $$ BEGIN
128
+ IF NOT EXISTS (SELECT 1 FROM pg_constraint c JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = c.conkey[1] WHERE c.conrelid = '"session"'::regclass AND c.contype = 'f' AND a.attname = 'userId') THEN
129
+ ALTER TABLE "session" ADD CONSTRAINT "session_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
130
+ END IF;
131
+ END $$;--> statement-breakpoint
132
+ CREATE INDEX IF NOT EXISTS "order_items_order" ON "order_items" USING btree ("order_id");--> statement-breakpoint
133
+ CREATE INDEX IF NOT EXISTS "orders_created" ON "orders" USING btree ("created_at" desc,"id" desc);--> statement-breakpoint
134
+ CREATE INDEX IF NOT EXISTS "orders_status" ON "orders" USING btree ("status","created_at" desc);--> statement-breakpoint
135
+ CREATE INDEX IF NOT EXISTS "orders_customer" ON "orders" USING btree ("customer_id");--> statement-breakpoint
136
+ CREATE INDEX IF NOT EXISTS "products_category" ON "products" USING btree ("category_id","price_cents","id");