Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
dd2adfc
fix(catalogue): fix 'chidlren' typo in GraphQL query builder
Plopix Mar 12, 2026
232dee0
fix(api-caller): remove dead try/catch block that only rethrows
Plopix Mar 12, 2026
c06d619
chore: delete dead commented-out subscription.ts file
Plopix Mar 12, 2026
8a68d8b
fix(catalogue): simplify double spread to single spread in product hy…
Plopix Mar 12, 2026
a6f03e9
fix(mass-call): use object literal instead of array for results initi…
Plopix Mar 12, 2026
6886cd7
fix(package): correct module field to match actual ESM build output
Plopix Mar 12, 2026
c0f86dc
fix(error): set name property on JSApiClientCallError for better debu…
Plopix Mar 12, 2026
662ec7d
fix(types): replace 'any' with proper GrabOptions type in grabber and…
Plopix Mar 12, 2026
8b2fe5f
fix(security): replace tracked .env with .env.example template
Plopix Mar 12, 2026
950ff2e
feat(auth): warn when no authentication credentials are configured
Plopix Mar 12, 2026
71c1297
fix(profiling): use regex to parse Server-Timing header reliably
Plopix Mar 12, 2026
e814167
refactor(types): rename cryptic generic parameters to descriptive nam…
Plopix Mar 12, 2026
d609b13
feat(client): add Symbol.dispose support for automatic cleanup
Plopix Mar 12, 2026
d43d11c
feat(client): add request timeout support via AbortController
Plopix Mar 13, 2026
eee2987
refactor(types): replace any with proper types in mass call client
Plopix Mar 13, 2026
f122d54
refactor(mass-call): replace repetitive enqueue methods with generate…
Plopix Mar 13, 2026
5bf0ea0
test: add unit tests with mocked HTTP for core modules
Plopix Mar 13, 2026
f669669
test: add comprehensive error-path tests for API caller
Plopix Mar 13, 2026
43aceeb
ci: add GitHub Actions workflow for PR and main branch checks
Plopix Mar 13, 2026
91ffb1c
docs: add JSDoc comments to all main exported factory functions
Plopix Mar 13, 2026
4b76e7b
feat: review
Plopix Mar 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions components/js-api-client/.claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"enabledMcpjsonServers": ["crystallize"]
}
12 changes: 12 additions & 0 deletions components/js-api-client/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# PROD
CRYSTALLIZE_TENANT_ID=your-tenant-id-here
CRYSTALLIZE_TENANT_IDENTIFIER=your-tenant-identifier-here
CRYSTALLIZE_ACCESS_TOKEN_ID=your-token-id-here
CRYSTALLIZE_ACCESS_TOKEN_SECRET=your-token-secret-here

# # DEV
# CRYSTALLIZE_TENANT_ID=your-dev-tenant-id-here
# CRYSTALLIZE_TENANT_IDENTIFIER=your-dev-tenant-identifier-here
# CRYSTALLIZE_ACCESS_TOKEN_ID=your-dev-token-id-here
# CRYSTALLIZE_ACCESS_TOKEN_SECRET=your-dev-token-secret-here
# CRYSTALLIZE_ORIGIN=-dev.crystallize.digital
37 changes: 37 additions & 0 deletions components/js-api-client/.github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: CI

on:
pull_request:
branches: [main]
push:
branches: [main]

jobs:
build-and-test:
name: Build & Unit Tests (Node ${{ matrix.node-version }})
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20, 22, 24]
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v5

- name: ⎔ Setup node
uses: actions/setup-node@v5
with:
node-version: ${{ matrix.node-version }}

- name: ⎔ Set up pnpm
uses: pnpm/action-setup@v4
with:
version: 10.14.0

- name: 📥 Download deps
run: pnpm install

- name: 🔨 Build
run: pnpm build

- name: 🧪 Run unit tests
run: pnpm vitest run tests/unit/
1 change: 1 addition & 0 deletions components/js-api-client/.gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
node_modules/
dist/
.env

yarn.lock
package-lock.json
12 changes: 12 additions & 0 deletions components/js-api-client/.mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"mcpServers": {
"crystallize": {
"type": "http",
"url": "https://mcp.crystallize.com/mcp",
"headers": {
"X-Crystallize-Access-Token-Id": "558f95141de1c4112f34",
"X-Crystallize-Access-Token-Secret": "8f56c78c874ce55b2629139b9061cefacaff7d17"
}
}
}
}
427 changes: 427 additions & 0 deletions components/js-api-client/PRD.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion components/js-api-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
},
"types": "./dist/index.d.ts",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"module": "./dist/index.js",
"devDependencies": {
"@tsconfig/node22": "^22.0.2",
"@types/node": "^24.2.0",
Expand Down
20 changes: 20 additions & 0 deletions components/js-api-client/progress.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
DX-003: Fix 'chidlren' typo in catalogue fetcher - DONE
DX-001: Remove dead try/catch block in API caller - DONE
DX-002: Delete commented-out subscription.ts file - DONE
DX-004: Remove unnecessary double spread in product hydrater - DONE
DX-005: Fix results variable initialization in mass call client - DONE
DX-006: Fix stale 'module' field in package.json - DONE
DX-007: Set error name on JSApiClientCallError - DONE
DX-008: Remove 'any' from union types in grabber and API caller signatures - DONE
DX-009: Replace tracked .env with .env.example - DONE
DX-010: Improve authentication error messaging - DONE
DX-014: Make Server-Timing header parsing robust - DONE
DX-011: Improve generic parameter naming in fetchers - DONE
DX-012: Add Symbol.dispose support for automatic cleanup - DONE
DX-013: Add request timeout support via AbortController - DONE
DX-016: Reduce any usage in mass call client types - DONE
DX-015: Reduce boilerplate in mass call client enqueue methods - DONE
DX-017: Add unit tests with mocked HTTP for core modules - DONE
DX-018: Add error-path tests for API caller - DONE
DX-019: Add CI workflow for pull requests - DONE
DX-020: Add JSDoc comments to main exported functions - DONE
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,25 @@ export type CatalogueFetcherGrapqhqlOnFolder<OC = unknown> = {
onChildren?: OC;
};

/**
* Creates a catalogue fetcher that executes queries against the Crystallize Catalogue API using JSON-based query objects.
* Use this when you want to build catalogue queries programmatically instead of writing raw GraphQL strings.
*
* @param client - A Crystallize client instance created via `createClient`.
* @returns A function that accepts a JSON query object and optional variables, and returns the catalogue data.
*
* @example
* ```ts
* const fetcher = createCatalogueFetcher(client);
* const data = await fetcher({
* catalogue: {
* __args: { path: '/my-product', language: 'en' },
* name: true,
* path: true,
* },
* });
* ```
*/
export const createCatalogueFetcher = (client: ClientInterface) => {
return <T = unknown>(query: object, variables?: VariablesType): Promise<T> => {
return client.catalogueApi<T>(jsonToGraphQLQuery({ query }), variables);
Expand Down Expand Up @@ -64,7 +83,7 @@ function onFolder<OF = unknown>(onFolder?: OF, c?: CatalogueFetcherGrapqhqlOnFol
const children = () => {
if (c?.onChildren) {
return {
chidlren: {
children: {
...c.onChildren,
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,20 @@ function buildNestedNavigationQuery(
return jsonToGraphQLQuery({ query });
}

/**
* Creates a navigation fetcher that builds nested tree queries for folder-based or topic-based navigation.
* Use this to retrieve hierarchical navigation structures from the Crystallize catalogue.
*
* @param client - A Crystallize client instance created via `createClient`.
* @returns An object with `byFolders` and `byTopics` methods for fetching navigation trees at a given depth.
*
* @example
* ```ts
* const nav = createNavigationFetcher(client);
* const folderTree = await nav.byFolders('/', 'en', 3);
* const topicTree = await nav.byTopics('/', 'en', 2);
* ```
*/
export function createNavigationFetcher(client: ClientInterface): {
byFolders: TreeFetcher;
byTopics: TreeFetcher;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ function byPaths(client: ClientInterface, options?: ProductHydraterOptions): Pro
}, {} as any);

const query = {
...{ ...productListQuery },
...productListQuery,
...(extraQuery !== undefined ? extraQuery : {}),
};

Expand Down Expand Up @@ -140,6 +140,21 @@ function bySkus(client: ClientInterface, options?: ProductHydraterOptions): Prod
};
}

/**
* Creates a product hydrater that fetches full product data from the catalogue by paths or SKUs.
* Use this to enrich a list of product references with complete variant, pricing, and attribute data.
*
* @param client - A Crystallize client instance created via `createClient`.
* @param options - Optional settings for market identifiers, price lists, and price-for-everyone inclusion.
* @returns An object with `byPaths` and `bySkus` methods for hydrating products.
*
* @example
* ```ts
* const hydrater = createProductHydrater(client);
* const products = await hydrater.byPaths(['/shop/my-product'], 'en');
* const productsBySkus = await hydrater.bySkus(['SKU-001', 'SKU-002'], 'en');
* ```
*/
export function createProductHydrater(client: ClientInterface, options?: ProductHydraterOptions) {
return {
byPaths: byPaths(client, options),
Expand Down
Loading
Loading