@spree/docs 0.1.306 → 0.1.307

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.
@@ -249,6 +249,20 @@ spree generate migration AddPositionToSpreeBrands position:integer # Rails buil
249
249
 
250
250
  `spree:api_resource` scaffolds the full v3 surface: model, migration, Store + Admin controllers and serializers, factory, controller specs, routes, and the `read_<resources>` / `write_<resources>` permissions that let staff roles and secret API keys reach the Admin API.
251
251
 
252
+ ### `spree filters types`
253
+
254
+ Generate TypeScript declarations for your app's list filters, so the SDKs accept and autocomplete the filters and sort fields your app adds. It reads them from the running app and writes one file per run, so run it once for each app that uses an SDK, and again after you change a filter.
255
+
256
+ ```bash
257
+ spree filters types --api store --out apps/storefront/src/types/spree-filters.d.ts # @spree/sdk
258
+ spree filters types --api admin --out apps/dashboard/src/types/spree-filters.d.ts # @spree/admin-sdk
259
+ spree filters types --api seller --out apps/seller-dashboard/src/types/spree-filters.d.ts # @spree/seller-sdk
260
+ ```
261
+
262
+ `--from <file>` reads the tables from `bin/rails spree:api:filter_tables` output instead of the running app.
263
+
264
+ See [using your own filters from TypeScript](../core-concepts/search-filtering.md#using-your-own-filters-from-typescript).
265
+
252
266
  ### `spree migrate`
253
267
 
254
268
  Install pending Spree migrations from gems, then run `db:migrate` — the canonical post-update sequence.
@@ -245,6 +245,91 @@ The Store API uses query parameters prefixed with `q[]` for filtering any resour
245
245
 
246
246
  > **INFO:** Only attributes explicitly allowed by each resource can be used for filtering. Attempting to filter on unsupported fields will be silently ignored.
247
247
 
248
+ ### Making a field filterable
249
+
250
+ A resource's filters come from its model's allowlists, and nowhere else. Spree generates its API reference and SDK types from them, so the [Querying](../../api-reference/admin-api/querying.md#which-filters-an-endpoint-accepts) reference lists what core declares. Filters your app adds take effect in the API immediately; to use them from TypeScript, declare them as shown below, since the published SDK types are generated from core alone.
251
+
252
+ ```ruby server/config/initializers/spree.rb
253
+ Spree.ransack.add_attribute(Spree::Product, :erp_id)
254
+ Spree.ransack.add_association(Spree::Product, :brand)
255
+ Spree.ransack.add_scope(Spree::Product, :featured, type: 'boolean')
256
+ Spree.ransack.add_scope(Spree::Product, :by_brand, type: 'id')
257
+ ```
258
+
259
+ Give a model a `search` filter (`q[search]=term`) by naming the attributes it matches, partially and case-insensitively. Attributes of associated records are named with the association as a prefix:
260
+
261
+ ```ruby server/app/models/spree/brand_decorator.rb
262
+ base.search_by :name, :code
263
+ ```
264
+
265
+ `search` is offered to the Store and Seller APIs only when each attribute it matches is one that API may filter on. The API reference describes it from the attributes.
266
+
267
+ Declare the `type` of a scope's argument: `'boolean'` for a scope taking none (`q[featured]=true`), or `'text'`, `'id'`, `'decimal'`, `'integer'`, `'date'` or `'datetime'` for one value. A list of kinds (`%w[decimal decimal]`) means several arguments in order, and `{ list: 'id' }` means any number of them. A scope declared without a type is published as taking one text value.
268
+
269
+ Every entry is a filter any caller of that API can run. Keep back-office fields out of the Store API with `private_ransackable_attributes`, and add associations the storefront may filter through to `storefront_ransackable_associations`.
270
+
271
+ ### Using your own filters from TypeScript
272
+
273
+ The SDKs type each `list()` call with the filters Spree itself declares. A filter your app adds works in the API at once, but TypeScript rejects it until it is declared, because the published types cannot know about it. There are three ways to use it.
274
+
275
+ **Generate the declarations (recommended).** This reads your app's filters, the ones Spree declares and the ones you added, and writes a declaration file for one SDK. Each frontend uses its own SDK, so generate a file for each app in your project, from the project root:
276
+
277
+ | App | SDK | `--api` |
278
+ |---|---|---|
279
+ | `apps/storefront` | `@spree/sdk` | `store` |
280
+ | `apps/dashboard` | `@spree/admin-sdk` | `admin` |
281
+ | `apps/seller-dashboard` (marketplaces) | `@spree/seller-sdk` | `seller` |
282
+
283
+ **Spree CLI:**
284
+
285
+ ```bash
286
+ spree filters types --api store --out apps/storefront/src/types/spree-filters.d.ts
287
+ spree filters types --api admin --out apps/dashboard/src/types/spree-filters.d.ts
288
+ spree filters types --api seller --out apps/seller-dashboard/src/types/spree-filters.d.ts
289
+ ```
290
+
291
+ **Without CLI:**
292
+
293
+ ```bash
294
+ cd server
295
+ bin/rails spree:api:filter_tables > ../filter-tables.json
296
+ cd ..
297
+ npx @spree/cli filters types --from filter-tables.json --api store --out apps/storefront/src/types/spree-filters.d.ts
298
+ npx @spree/cli filters types --from filter-tables.json --api admin --out apps/dashboard/src/types/spree-filters.d.ts
299
+ npx @spree/cli filters types --from filter-tables.json --api seller --out apps/seller-dashboard/src/types/spree-filters.d.ts
300
+ ```
301
+
302
+
303
+ Run them again after you add or change a filter, and commit the generated files with the app code that uses them. Match `--api` to the app: a storefront file generated with `--api admin` compiles, but declares the Admin API's filters. After that, every `list()` call accepts and autocompletes your filters and sort fields, and a misspelled one is still a compile error.
304
+
305
+ **Send it unchecked.** Filters inside `q` are sent as given, without type checking or autocomplete:
306
+
307
+ ```typescript
308
+ await client.products.list({
309
+ name_cont: 'shirt',
310
+ q: { erp_id_eq: 'ERP-1' },
311
+ })
312
+ ```
313
+
314
+ **Declare it by hand.** Each resource has an empty interface in the SDK (`ProductFilterExtensions`, `OrderFilterExtensions`, …) that is part of its filter type, and TypeScript [merges your declaration](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation) into it. The file must contain an `import` or `export`; without one, it replaces the SDK's types instead of adding to them:
315
+
316
+ ```typescript src/types/spree.d.ts
317
+ export {}
318
+
319
+ declare module '@spree/sdk' {
320
+ interface ProductFilterExtensions {
321
+ erp_id_eq?: string
322
+ featured?: boolean
323
+ }
324
+ // Sort fields, as keys
325
+ interface ProductSortExtensions {
326
+ erp_id: true
327
+ }
328
+ }
329
+ ```
330
+
331
+ Name each filter the way the API receives it, with its predicate (`erp_id_eq`, `erp_id_in` for a list) or a scope by its name. Type amounts as decimal strings, counts as numbers and flags as booleans.
332
+
248
333
  ## Pagination
249
334
 
250
335
  All list endpoints support pagination:
@@ -169,7 +169,7 @@ attribute(:brand_id) { |product| product.brand&.prefixed_id }
169
169
  has_one :brand, serializer: Spree::Api::V3::BrandSerializer
170
170
  ```
171
171
 
172
- The model has to allow filtering by it, or `?q[brand_id_eq]=…` is rejected:
172
+ The model has to allow filtering by it, or `?q[brand_id_eq]=…` is ignored:
173
173
 
174
174
  ```ruby server/app/models/spree/product_decorator.rb
175
175
  base.whitelisted_ransackable_attributes |= %w[brand_id]
@@ -992,6 +992,55 @@ Both internal-note serializers now return the pair. Previously orders exposed on
992
992
 
993
993
  Stored markup is also held to a narrower allowlist than 5.6 — see [the rich-text migration](#move-rich-text-out-of-action-text) for what is permitted and how to widen it.
994
994
 
995
+ ## Upgrading a storefront
996
+
997
+ A storefront built on the Store API and `@spree/sdk`, such as the [Spree storefront](https://github.com/spree/storefront), needs `@spree/sdk` 2.0 and the changes below. Most are type errors that point at the line to change.
998
+
999
+ ### List filters and sort are typed
1000
+
1001
+ Each `list()` method now takes the filters and sort fields its endpoint accepts. A misspelled filter, a filter the endpoint doesn't support, or an unknown sort field used to return the unfiltered list; it is now a compile error. `ProductListParams`, `CategoryListParams`, `CollectionListParams` and `OrderListParams` keep their names but no longer accept arbitrary keys.
1002
+
1003
+ - **A sort value read from the URL is a plain `string`.** Narrow it where you read it; the API still validates the value at runtime:
1004
+
1005
+ ```ts src/lib/utils/product-query.ts
1006
+ import type { ProductListParams, ProductSort } from '@spree/sdk'
1007
+
1008
+ params.sort = filters.sortBy as ProductSort
1009
+ ```
1010
+
1011
+ - **Filters your backend adds are compile errors until you declare them.** The published types list only the filters Spree itself declares. Any filter or scope your backend adds (through `Spree.ransack.add_attribute`, `add_scope` or a model decorator) still works in the API, but TypeScript rejects the key. Generate the declarations from your app, and run the command again whenever a filter changes:
1012
+
1013
+ ```bash
1014
+ spree filters types --api store --out apps/storefront/src/types/spree-filters.d.ts
1015
+ ```
1016
+
1017
+ Generate one for the dashboard (`--api admin --out apps/dashboard/src/types/spree-filters.d.ts`) and the seller panel (`--api seller`) too, if they use your filters. For a one-off filter, send it inside `q` instead (`list({ q: { erp_id_eq: 'ERP-1' } })`), which is not type checked. See [using your own filters from TypeScript](../core-concepts/search-filtering.md#using-your-own-filters-from-typescript) for both, and for declaring filters by hand.
1018
+
1019
+ - **Amount filters take decimal strings.** `price_gte`, `price_lte` and the other money filters are typed as strings, like every money value in 6.0. Send `'20.00'`, not `20`.
1020
+
1021
+ - **Prefer `search`.** Products, categories and collections take `q[search]` (the SDK's `search` key), which matches more than `name_cont`. See [which filters an endpoint accepts](../../api-reference/store-api/querying.md#which-filters-an-endpoint-accepts).
1022
+
1023
+ ### Completing a cart can return an order group
1024
+
1025
+ `carts.complete()` returns `Order | OrderGroup`. In a marketplace, a cart holding several sellers' goods divides into one order per seller and returns the group. Narrow the result with `isOrderGroup` before reading order fields:
1026
+
1027
+ ```ts
1028
+ import { isOrderGroup } from '@spree/sdk'
1029
+
1030
+ const result = await client.carts.complete(cartId)
1031
+ const orders = isOrderGroup(result) ? result.orders : [result]
1032
+ ```
1033
+
1034
+ ### Other Store API changes to check
1035
+
1036
+ | Change | What to update | Details |
1037
+ |---|---|---|
1038
+ | Delivery requirement renamed | Match `delivery_method` (field) and `delivery_method_required` (code) instead of `shipping_method` | [Checkout without a state machine](#checkout-without-a-state-machine) |
1039
+ | Type values are short names | Compare `payment_source_type` with `credit_card`, and read custom field `field_type` | [Type values are short names](#type-values-are-short-names-not-class-names) |
1040
+ | Rich text comes in two shapes | Render `description_html` for markup; `description` is plain text | [Rich-text fields](#rich-text-fields-read-as-plain-text-plus-html) |
1041
+ | Payment source IDs | Stored `source_id` values change prefix from `ps_` to `psrc_` | [Payment source IDs](#payment-source-ids-use-the-psrc_-prefix) |
1042
+ | Completed carts are read-only | Read post-checkout data from the order, not the cart | [The Cart/Order split](#the-cartorder-split) |
1043
+
995
1044
  ## For extension authors
996
1045
 
997
1046
  - **Don't reach for model business methods from services** — 6.0 code style writes behavior inline in service/workflow steps; models keep data, validations, predicates and persistence primitives. Extensions patching removed model methods (`finalize!` internals, updater hooks) should move to workflow hooks (`Spree.hooks.register('carts.complete.before_finalize') { |flow| ... }` — handlers receive the workflow instance) or event subscribers.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spree/docs",
3
- "version": "0.1.306",
3
+ "version": "0.1.307",
4
4
  "description": "Spree Commerce developer documentation for AI agents and local reference",
5
5
  "type": "module",
6
6
  "license": "CC-BY-4.0",