fix: replace hand-scaffolded Functions with real CLI-generated ones

The user linked the app to a real Partner org and hit two live errors
running `shopify app dev`, which is exactly the verification the earlier
hand-scaffolded Functions couldn't get in this environment. Root-caused and
fixed both, then went further: regenerated both Functions from scratch via
`shopify app generate extension` (now possible — the user's session had
authenticated) instead of patching the guesses.

What broke and why:
- `shopify app config link` pulled a fresh app's (empty) remote config and
  overwrote shopify.app.toml, dropping the webhook subscriptions and
  app_proxy block — restored both, keeping the real client_id/name/scopes
  the CLI set.
- `[extensions.build.watch]` as a nested table was invalid TOML for this
  field — it's a plain `watch = [...]` array directly under
  `[extensions.build]`.
- The real failure ("doesn't have a build command or it's empty") turned
  out to be a red herring pointing at a stale filename
  (shopify.function.extension.toml, not the current shopify.extension.toml)
  — the actual problem was that `@shopify/shopify_function` was never
  installed for these extensions (confirmed: no node_modules), because
  hand-writing package.json doesn't run the install step
  `shopify app generate extension` does automatically.

Rather than keep guessing at the toolchain, regenerated both Functions for
real:
- `shopify app generate extension --template=cart_checkout_validation` and
  `--template=delivery_customization` (--flavor=vanilla-js), which produces
  a working vite/vitest-based build+test setup, real
  `@shopify/shopify-function-test-helpers` fixture testing (builds actual
  WASM and runs it via function-runner), and a generated GraphQL type file
  per extension.
- This surfaced several concrete corrections to what was hand-written
  before: the real target names are `cart.validations.generate.run` and
  `cart.delivery-options.transform.run` (not `purchase.validation.run` /
  `purchase.delivery-customization.run`), current api_version is 2026-07
  (not 2025-01), the validation output wraps errors in
  `operations: [{ validationAdd: { errors } }]` with a plain `message`
  field (not top-level `errors` with `localizedMessage`), and the rename
  operation is `deliveryOptionRename: { deliveryOptionHandle, title }` (not
  `rename: { deliveryOptionHandle, title }`).
- Rewrote each extension's `.graphql` input query to request our actual
  dd_* cart attributes (plus delivery option handles for the rename case),
  regenerated types via `npm run typegen` in each, and ported the pure
  evaluate.js decision logic (same exported function names/behavior as
  before, now proven correct against the live schema) into the adapter
  file the generator expects.
- Replaced each extension's demo fixture with ones matching our real
  logic; `npm test` inside each extension now compiles real WASM and runs
  function-runner against them — this is strictly stronger verification
  than the previous pure-JS-only unit tests (which are kept too, unchanged,
  since the evaluate.js files kept the same interface).

Repo-wide wiring: extensions/*/generated and extensions/*/dist are not
committed (matches the CLI's own per-extension .gitignore) — added
`npm run typegen:functions` (runs automatically before `npm run typecheck`
via a pretypecheck hook) and `npm run test:functions`, both now also in CI.
Root `npm install` picked these two folders up as proper npm workspace
members (they already have their own package.json from generation).

Verified: lint, typecheck, all 52 unit tests, all 8 integration tests, both
extensions' real WASM/function-runner test suites (5 fixtures total), and
both `npm run build` / `npm run build:widget` all pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
metatroncubeswdev 2026-08-23 20:14:21 -04:00
parent 7a340ad135
commit 381c52d01a
37 changed files with 12040 additions and 363 deletions

View File

@ -5,3 +5,4 @@ shopify-app-remix
*/*.yml
.shopify
extensions/*/assets/*.js
extensions/*/dist

View File

@ -48,6 +48,8 @@ jobs:
run: npm run typecheck
- name: Unit tests
run: npm test -- --run
- name: Function tests (real WASM build + function-runner against fixtures)
run: npm run test:functions
- name: Apply migrations
run: npx prisma migrate deploy
- name: Integration tests (Redis-backed slot-hold concurrency, Booking flow)

View File

@ -35,6 +35,8 @@ runs the BullMQ worker (jobs/worker.ts) once Phase 4 makes it do anything.
| `npm run deploy` | `shopify app deploy` — deploy extensions/functions |
| `npm run worker` | BullMQ worker (hold-expiry, notifications) |
| `npm run test:integration` | Redis/Postgres-backed tests (slot-hold concurrency, booking flow) — needs `docker compose up -d` |
| `npm run test:functions` | Real WASM build + `function-runner` tests for both Shopify Functions, against fixtures |
| `npm run typegen:functions` | Regenerate `extensions/*/generated/api.ts` from each Function's `schema.graphql` + `.graphql` query (also runs automatically before `npm run typecheck`) |
## Status
@ -50,10 +52,13 @@ automatically before `npm run dev` / `npm run deploy`.
**Functions are JavaScript, not Rust** (`extensions/validation-slot/`,
`extensions/delivery-customization/`) — no Rust toolchain was available in
the environment that built Phase 4, and `IMPLEMENTATION_PLAN.md` §1
explicitly allows JS as a fallback. They (and the widget's Theme App
Extension) were hand-scaffolded rather than generated via
`shopify app generate extension`, which needs an interactive Partner login.
**Before deploying either Function**, run `shopify app function schema` in
its directory to pull the authoritative `schema.graphql` for your API
version and confirm `src/run.graphql` still matches it — these were written
from documented conventions, not validated against a live schema.
explicitly allows JS as a fallback. Both were generated with
`shopify app generate extension` (once a real Partner login was available)
and their business logic (`src/evaluate.js` in each) is verified two ways:
plain Vitest unit tests at the repo root (`npm test`) and real
`function-runner` fixture tests that compile actual WASM
(`npm run test:functions`, also in CI). `extensions/*/generated/` and
`extensions/*/dist/` aren't committed (matching the CLI's own
`.gitignore` for these extensions) — `npm run typegen:functions`
regenerates the types from the committed `schema.graphql`, and building
runs automatically as part of `npm run dev` / `test:functions`.

View File

@ -1,2 +1,3 @@
name = "Delivery Date & Time"
type = "theme"
uid = "4935f147-fc17-827b-2629-385b36917fa08faa33de"

View File

@ -0,0 +1,2 @@
dist
generated

View File

@ -0,0 +1,4 @@
{
"name": "Delivery Method Labeling",
"description": "Relabels delivery options at checkout with the shopper's chosen pickup/delivery date and time."
}

View File

@ -1,12 +1,35 @@
{
"name": "delivery-customization",
"version": "1.0.0",
"private": true,
"version": "0.0.1",
"license": "UNLICENSED",
"type": "module",
"scripts": {
"build": "shopify-function-build"
"shopify": "npm exec -- shopify",
"typegen": "npm exec -- shopify app function typegen",
"build": "npm exec -- shopify app function build",
"preview": "npm exec -- shopify app function run",
"test": "vitest"
},
"codegen": {
"schema": "schema.graphql",
"documents": "src/*.graphql",
"generates": {
"./generated/api.ts": {
"plugins": [
"typescript",
"typescript-operations"
]
}
},
"config": {
"omitOperationSuffix": true
}
},
"dependencies": {
"@shopify/shopify_function": "^1.0.0"
"@shopify/shopify_function": "^2.0.1"
},
"devDependencies": {
"@shopify/shopify-function-test-helpers": "^1.0.0",
"vitest": "^3.2.4"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +1,21 @@
# NOTE: hand-written — see validation-slot/shopify.extension.toml for why,
# and run `shopify app function schema` before deploying to confirm
# src/run.graphql matches the live schema for your API version.
api_version = "2025-01"
api_version = "2026-07"
[[extensions]]
name = "Delivery Method Labeling"
name = "t:name"
handle = "delivery-customization"
type = "function"
uid = "7caef09d-bc92-c4fe-9c8c-5e43dc1b38bce5a7fa55"
description = "t:description"
[[extensions.targeting]]
target = "purchase.delivery-customization.run"
input_query = "src/run.graphql"
export = "deliveryCustomizationRun"
target = "cart.delivery-options.transform.run"
input_query = "src/cart_delivery_options_transform_run.graphql"
export = "cart-delivery-options-transform-run"
[extensions.build]
command = ""
path = "dist/function.wasm"
[extensions.build.watch]
paths = ["src/**/*.js"]
[extensions.ui.paths]
create = "/"
details = "/"

View File

@ -1,4 +1,4 @@
query RunInput {
query CartDeliveryOptionsTransformRunInput {
cart {
ddMethod: attribute(key: "dd_method") {
value

View File

@ -0,0 +1,48 @@
// @ts-check
import { renameLabelFor } from "./evaluate.js";
/**
* @typedef {import("../generated/api").CartDeliveryOptionsTransformRunInput} CartDeliveryOptionsTransformRunInput
* @typedef {import("../generated/api").CartDeliveryOptionsTransformRunResult} CartDeliveryOptionsTransformRunResult
*/
/**
* @type {CartDeliveryOptionsTransformRunResult}
*/
const NO_CHANGES = {
operations: [],
};
/**
* Relabels every presented delivery option to the shopper's actual chosen
* method + date, so checkout never shows a generic carrier label
* ("Standard", "Economy") that could be mistaken for a shipping ETA on
* what's actually a pickup or local-delivery order. Phase 4 doesn't yet
* have a per-option method mapping (that needs Phase 5's zones/rates
* work), so every option in the cart gets the same clarified label
* reasonable since a single order only has one chosen fulfillment method
* today.
* @param {CartDeliveryOptionsTransformRunInput} input
* @returns {CartDeliveryOptionsTransformRunResult}
*/
export function cartDeliveryOptionsTransformRun(input) {
const decision = renameLabelFor({
dd_method: input.cart.ddMethod?.value,
dd_date: input.cart.ddDate?.value,
});
if (!decision.rename) {
return NO_CHANGES;
}
const operations = input.cart.deliveryGroups.flatMap((group) =>
group.deliveryOptions.map((option) => ({
deliveryOptionRename: {
deliveryOptionHandle: option.handle,
title: decision.title,
},
})),
);
return { operations };
};

View File

@ -1,6 +1,8 @@
// @ts-check
// Pure decision logic — see validation-slot/src/evaluate.js for why this is
// split out from run.js (unit-testable without a WASM build).
// Pure decision logic, kept separate from the run.js adapter so it's
// unit-testable directly with plain Vitest — no WASM build needed to verify
// the actual rule (see tests/unit/delivery-customization.test.ts at the
// repo root).
//
// This directly targets the DS review complaint PRODUCT_STRATEGY.md §3.1
// names: "a stray 'estimated delivery date' on a pickup order." Renaming

View File

@ -0,0 +1 @@
export * from './cart_delivery_options_transform_run';

View File

@ -1,42 +0,0 @@
// @ts-check
import { renameLabelFor } from "./evaluate.js";
/**
* @typedef {import("../generated/api").CartInput} RunInput
* @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult
*/
/**
* Delivery Customization Function: relabels every presented delivery
* option to the shopper's actual chosen method + date, so checkout never
* shows a generic carrier label ("Standard", "Economy") that could be
* mistaken for a shipping ETA on what's actually a pickup or local-delivery
* order. Phase 4 doesn't yet have a per-option method mapping (that needs
* Phase 5's zones/rates work), so every option in the cart gets the same
* clarified label reasonable since a single order only has one chosen
* fulfillment method today.
* @param {RunInput} input
* @returns {FunctionRunResult}
*/
export function deliveryCustomizationRun(input) {
const attributes = {
dd_method: input.cart.ddMethod?.value,
dd_date: input.cart.ddDate?.value,
};
const decision = renameLabelFor(attributes);
if (!decision.rename) {
return { operations: [] };
}
const operations = input.cart.deliveryGroups.flatMap((group) =>
group.deliveryOptions.map((option) => ({
rename: {
deliveryOptionHandle: option.handle,
title: decision.title,
},
})),
);
return { operations };
}

View File

@ -0,0 +1,45 @@
import path from "path";
import fs from "fs";
import { describe, beforeAll, test, expect } from "vitest";
import { buildFunction, getFunctionInfo, loadSchema, loadInputQuery, loadFixture, validateTestAssets, runFunction } from "@shopify/shopify-function-test-helpers";
describe("Default Integration Test", () => {
let schema;
let functionDir;
let functionInfo;
let schemaPath;
let targeting;
let functionRunnerPath;
let wasmPath;
beforeAll(async () => {
functionDir = path.dirname(__dirname);
await buildFunction(functionDir);
functionInfo = await getFunctionInfo(functionDir);
({ schemaPath, functionRunnerPath, wasmPath, targeting } = functionInfo);
schema = await loadSchema(schemaPath);
}, 45000);
const fixturesDir = path.join(__dirname, "fixtures");
const fixtureFiles = fs
.readdirSync(fixturesDir)
.filter((file) => file.endsWith(".json"))
.map((file) => path.join(fixturesDir, file));
fixtureFiles.forEach((fixtureFile) => {
test(`runs ${path.relative(fixturesDir, fixtureFile)}`, async () => {
const fixture = await loadFixture(fixtureFile);
const targetInputQueryPath = targeting[fixture.target].inputQueryPath;
const inputQueryAST = await loadInputQuery(targetInputQueryPath);
const validationResult = await validateTestAssets({ schema, fixture, inputQueryAST });
expect(validationResult.inputQuery.errors).toEqual([]);
expect(validationResult.inputFixture.errors).toEqual([]);
expect(validationResult.outputFixture.errors).toEqual([]);
const runResult = await runFunction(fixture, functionRunnerPath, wasmPath, targetInputQueryPath, schemaPath);
expect(runResult.error).toBeNull();
expect(runResult.result.output).toEqual(fixture.expectedOutput);
}, 10000);
});
});

View File

@ -0,0 +1,20 @@
{
"payload": {
"export": "cart-delivery-options-transform-run",
"target": "cart.delivery-options.transform.run",
"input": {
"cart": {
"ddMethod": null,
"ddDate": null,
"deliveryGroups": [
{
"deliveryOptions": [{ "handle": "standard-shipping" }]
}
]
}
},
"output": {
"operations": []
}
}
}

View File

@ -0,0 +1,33 @@
{
"payload": {
"export": "cart-delivery-options-transform-run",
"target": "cart.delivery-options.transform.run",
"input": {
"cart": {
"ddMethod": { "value": "PICKUP" },
"ddDate": { "value": "2026-08-25" },
"deliveryGroups": [
{
"deliveryOptions": [{ "handle": "standard-shipping" }, { "handle": "express-shipping" }]
}
]
}
},
"output": {
"operations": [
{
"deliveryOptionRename": {
"deliveryOptionHandle": "standard-shipping",
"title": "Pickup — 2026-08-25"
}
},
{
"deliveryOptionRename": {
"deliveryOptionHandle": "express-shipping",
"title": "Pickup — 2026-08-25"
}
}
]
}
}
}

View File

@ -0,0 +1 @@
// Prevents inheritance from parent Remix project

View File

@ -0,0 +1,8 @@
export default {
test: {
forceRerunTriggers: [
'**/tests/fixtures/**',
'**/src/**',
],
},
};

2
extensions/validation-slot/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
dist
generated

View File

@ -0,0 +1,4 @@
{
"name": "Delivery Slot Validation",
"description": "Blocks checkout if no delivery date/time slot was selected."
}

View File

@ -1,12 +1,35 @@
{
"name": "validation-slot",
"version": "1.0.0",
"private": true,
"version": "0.0.1",
"license": "UNLICENSED",
"type": "module",
"scripts": {
"build": "shopify-function-build"
"shopify": "npm exec -- shopify",
"typegen": "npm exec -- shopify app function typegen",
"build": "npm exec -- shopify app function build",
"preview": "npm exec -- shopify app function run",
"test": "vitest"
},
"codegen": {
"schema": "schema.graphql",
"documents": "src/*.graphql",
"generates": {
"./generated/api.ts": {
"plugins": [
"typescript",
"typescript-operations"
]
}
},
"config": {
"omitOperationSuffix": true
}
},
"dependencies": {
"@shopify/shopify_function": "^1.0.0"
"@shopify/shopify_function": "^2.0.1"
},
"devDependencies": {
"@shopify/shopify-function-test-helpers": "^1.0.0",
"vitest": "^3.2.4"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,27 +1,17 @@
# NOTE: hand-written (shopify app generate extension needs an interactive
# Partner login unavailable in this environment — see README.md). This
# mirrors Shopify's documented structure for a JS Cart/Checkout Validation
# Function as closely as possible from training knowledge, but has not been
# validated against a live `shopify app function schema` pull. Before
# deploying: run `shopify app function schema` in this directory to fetch
# the authoritative schema.graphql for your app's API version, confirm
# src/run.graphql still matches it, and fix up anything that's drifted.
api_version = "2025-01"
api_version = "2026-07"
[[extensions]]
name = "Delivery Slot Validation"
name = "t:name"
handle = "validation-slot"
type = "function"
uid = "f5265178-4064-010b-5acf-a7b68096a1d29be8d5da"
description = "t:description"
[[extensions.targeting]]
target = "purchase.validation.run"
input_query = "src/run.graphql"
export = "cartCheckoutValidationRun"
target = "cart.validations.generate.run"
input_query = "src/cart_validations_generate_run.graphql"
export = "cart-validations-generate-run"
[extensions.build]
command = ""
path = "dist/function.wasm"
[extensions.build.watch]
paths = ["src/**/*.js"]

View File

@ -1,4 +1,4 @@
query RunInput {
query CartValidationsGenerateRunInput {
cart {
ddMethod: attribute(key: "dd_method") {
value

View File

@ -2,8 +2,8 @@
import { evaluateCartAttributes } from "./evaluate.js";
/**
* @typedef {import("../generated/api").CartInput} RunInput
* @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult
* @typedef {import("../generated/api").CartValidationsGenerateRunInput} CartValidationsGenerateRunInput
* @typedef {import("../generated/api").CartValidationsGenerateRunResult} CartValidationsGenerateRunResult
*/
const ERROR_MESSAGE = {
@ -14,10 +14,10 @@ const ERROR_MESSAGE = {
/**
* Cart & Checkout Validation Function the server-side enforcement half of
* the widget/Function split (see evaluate.js for the full scope note).
* @param {RunInput} input
* @returns {FunctionRunResult}
* @param {CartValidationsGenerateRunInput} input
* @returns {CartValidationsGenerateRunResult}
*/
export function cartCheckoutValidationRun(input) {
export function cartValidationsGenerateRun(input) {
const attributes = {
dd_method: input.cart.ddMethod?.value,
dd_date: input.cart.ddDate?.value,
@ -28,16 +28,13 @@ export function cartCheckoutValidationRun(input) {
const result = evaluateCartAttributes(attributes);
if (result.valid) {
return { errors: [] };
}
return {
errors: [
operations: [
{
localizedMessage: ERROR_MESSAGE[result.reason],
target: "cart",
validationAdd: {
errors: result.valid ? [] : [{ message: ERROR_MESSAGE[result.reason], target: "$.cart" }],
},
},
],
};
}
};

View File

@ -1,11 +1,14 @@
// @ts-check
// Pure decision logic, kept separate from run.js so it can be unit-tested
// directly with plain Vitest — no WASM build or function-runner needed to
// verify the actual rule. This is the enforcement half of the "widget
// collects, Function enforces" split (CLAUDE.md non-negotiable): a shopper
// who clears the cart attribute, calls the cart API directly, or otherwise
// bypasses the storefront widget still cannot complete checkout, because
// this runs server-side inside Shopify's own checkout, not in the browser.
// Pure decision logic, kept separate from the run.js adapter so it can be
// unit-tested directly with plain Vitest (see
// tests/unit/validation-slot.test.ts at the repo root) as well as through
// the real function-runner fixtures in tests/fixtures/ here.
//
// This is the enforcement half of the "widget collects, Function enforces"
// split (CLAUDE.md non-negotiable): a shopper who clears the cart
// attribute, calls the cart API directly, or otherwise bypasses the
// storefront widget still cannot complete checkout, because this runs
// server-side inside Shopify's own checkout, not in the browser.
//
// Scope note (Phase 4): every order on shops that activate this Function is
// currently treated as requiring a schedule selection — there's no
@ -17,8 +20,8 @@
// capacity snapshot at the moment checkout completes ("has since been
// taken" in PRODUCT_STRATEGY.md §3.1) — Functions can't call our DB, and
// building a metafield-snapshot refresh pipeline for that is real,
// unscoped work. The 10-minute hold TTL (holds.server.ts) is the mitigation
// for that specific race in the meantime.
// unscoped work. The 10-minute hold TTL (app/services/holds.server.ts) is
// the mitigation for that specific race in the meantime.
const REQUIRED_ATTRIBUTES = ["dd_method", "dd_date", "dd_start_min", "dd_end_min", "dd_location_id"];

View File

@ -0,0 +1 @@
export * from './cart_validations_generate_run';

View File

@ -0,0 +1,45 @@
import path from "path";
import fs from "fs";
import { describe, beforeAll, test, expect } from "vitest";
import { buildFunction, getFunctionInfo, loadSchema, loadInputQuery, loadFixture, validateTestAssets, runFunction } from "@shopify/shopify-function-test-helpers";
describe("Default Integration Test", () => {
let schema;
let functionDir;
let functionInfo;
let schemaPath;
let targeting;
let functionRunnerPath;
let wasmPath;
beforeAll(async () => {
functionDir = path.dirname(__dirname);
await buildFunction(functionDir);
functionInfo = await getFunctionInfo(functionDir);
({ schemaPath, functionRunnerPath, wasmPath, targeting } = functionInfo);
schema = await loadSchema(schemaPath);
}, 45000);
const fixturesDir = path.join(__dirname, "fixtures");
const fixtureFiles = fs
.readdirSync(fixturesDir)
.filter((file) => file.endsWith(".json"))
.map((file) => path.join(fixturesDir, file));
fixtureFiles.forEach((fixtureFile) => {
test(`runs ${path.relative(fixturesDir, fixtureFile)}`, async () => {
const fixture = await loadFixture(fixtureFile);
const targetInputQueryPath = targeting[fixture.target].inputQueryPath;
const inputQueryAST = await loadInputQuery(targetInputQueryPath);
const validationResult = await validateTestAssets({ schema, fixture, inputQueryAST });
expect(validationResult.inputQuery.errors).toEqual([]);
expect(validationResult.inputFixture.errors).toEqual([]);
expect(validationResult.outputFixture.errors).toEqual([]);
const runResult = await runFunction(fixture, functionRunnerPath, wasmPath, targetInputQueryPath, schemaPath);
expect(runResult.error).toBeNull();
expect(runResult.result.output).toEqual(fixture.expectedOutput);
}, 10000);
});
});

View File

@ -0,0 +1,24 @@
{
"payload": {
"export": "cart-validations-generate-run",
"target": "cart.validations.generate.run",
"input": {
"cart": {
"ddMethod": { "value": "PICKUP" },
"ddDate": { "value": "2026-08-25" },
"ddStartMin": { "value": "540" },
"ddEndMin": { "value": "600" },
"ddLocationId": { "value": "loc_123" }
}
},
"output": {
"operations": [
{
"validationAdd": {
"errors": []
}
}
]
}
}
}

View File

@ -0,0 +1,29 @@
{
"payload": {
"export": "cart-validations-generate-run",
"target": "cart.validations.generate.run",
"input": {
"cart": {
"ddMethod": { "value": "PICKUP" },
"ddDate": { "value": "2026-08-25" },
"ddStartMin": null,
"ddEndMin": null,
"ddLocationId": { "value": "loc_123" }
}
},
"output": {
"operations": [
{
"validationAdd": {
"errors": [
{
"message": "Your delivery date/time selection is incomplete — please choose it again.",
"target": "$.cart"
}
]
}
}
]
}
}
}

View File

@ -0,0 +1,29 @@
{
"payload": {
"export": "cart-validations-generate-run",
"target": "cart.validations.generate.run",
"input": {
"cart": {
"ddMethod": null,
"ddDate": null,
"ddStartMin": null,
"ddEndMin": null,
"ddLocationId": null
}
},
"output": {
"operations": [
{
"validationAdd": {
"errors": [
{
"message": "Please choose a delivery date and time before checking out.",
"target": "$.cart"
}
]
}
}
]
}
}
}

View File

@ -0,0 +1 @@
// Prevents inheritance from parent Remix project

View File

@ -0,0 +1,8 @@
export default {
test: {
forceRerunTriggers: [
'**/tests/fixtures/**',
'**/src/**',
],
},
};

1083
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,10 @@
"docker-start": "npm run setup && npm run start",
"setup": "prisma generate && prisma migrate deploy",
"lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .",
"typegen:functions": "npm --prefix extensions/validation-slot run typegen && npm --prefix extensions/delivery-customization run typegen",
"pretypecheck": "npm run typegen:functions",
"typecheck": "tsc --noEmit",
"test:functions": "npm --prefix extensions/validation-slot test && npm --prefix extensions/delivery-customization test",
"test": "vitest",
"test:integration": "vitest run --config vitest.integration.config.ts",
"test:e2e": "playwright test",

View File

@ -1,25 +1,19 @@
# This file stores configurations for your Shopify app.
# Learn more at https://shopify.dev/docs/apps/tools/cli/configuration
# Learn more about configuring your app at https://shopify.dev/docs/apps/tools/cli/configuration
client_id = ""
name = "delivery-datetime-app"
application_url = "https://replace-with-your-tunnel-url.example.com"
client_id = "890f611da9f31c1a8e3183b5300b2f53"
name = "Metatron-delivery"
application_url = "https://shopify.dev/apps/default-app-home"
embedded = true
[access_scopes]
# Minimum scopes for the v1 (Phase 0-4) feature set. Add more only when a
# feature in IMPLEMENTATION_PLAN.md actually needs it.
scopes = "read_products,read_customers,read_orders,write_orders,read_locations,read_metaobjects,write_metaobjects,write_cart_transforms,write_delivery_customizations,write_payment_customizations,read_markets,read_locales"
# Learn more at https://shopify.dev/docs/apps/tools/cli/configuration#access_scopes
scopes = "read_customers,read_locales,read_locations,read_markets,read_metaobjects,read_orders,read_products,write_cart_transforms,write_delivery_customizations,write_metaobjects,write_orders,write_payment_customizations"
[auth]
redirect_urls = [
"https://replace-with-your-tunnel-url.example.com/auth/callback",
"https://replace-with-your-tunnel-url.example.com/auth/shopify/callback",
"https://replace-with-your-tunnel-url.example.com/api/auth/callback"
]
redirect_urls = [ "https://shopify.dev/apps/default-app-home/api/auth" ]
[webhooks]
api_version = "2024-10"
api_version = "2026-10"
# Handled by: app/routes/webhooks.app.uninstalled.tsx
[[webhooks.subscriptions]]
@ -31,17 +25,17 @@ api_version = "2024-10"
uri = "/webhooks/app/scopes_update"
topics = ["app/scopes_update"]
# Handled by: app/routes/webhooks.orders.create.tsx (Phase 4)
# Handled by: app/routes/webhooks.orders.create.tsx
[[webhooks.subscriptions]]
uri = "/webhooks/orders/create"
topics = ["orders/create"]
# Handled by: app/routes/webhooks.orders.updated.tsx (Phase 4)
# Handled by: app/routes/webhooks.orders.updated.tsx
[[webhooks.subscriptions]]
uri = "/webhooks/orders/updated"
topics = ["orders/updated"]
# Handled by: app/routes/webhooks.orders.cancelled.tsx (Phase 4)
# Handled by: app/routes/webhooks.orders.cancelled.tsx
[[webhooks.subscriptions]]
uri = "/webhooks/orders/cancelled"
topics = ["orders/cancelled"]
@ -63,7 +57,12 @@ api_version = "2024-10"
compliance_topics = ["shop/redact"]
# App proxy so the storefront Theme App Extension can call our backend
# without CORS issues (see IMPLEMENTATION_PLAN.md §5.3).
# without CORS issues (see IMPLEMENTATION_PLAN.md §5.3). `shopify app dev`
# points this at your dev tunnel automatically when
# automatically_update_urls_on_dev is true (see [build] below); if it
# doesn't, set it manually to `<your-tunnel-url>/apps/scheduling` — the
# Remix routes it forwards to (apps.scheduling.*.tsx) assume that exact
# prefix.
[app_proxy]
url = "https://replace-with-your-tunnel-url.example.com/apps/scheduling"
subpath = "scheduling"
@ -71,3 +70,4 @@ prefix = "apps"
[build]
include_config_on_deploy = true
automatically_update_urls_on_dev = true