@curless/shopify-storefront 0.4.1 → 0.6.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.
- package/dist/__tests__/listed-pages.test.d.ts +2 -0
- package/dist/__tests__/listed-pages.test.d.ts.map +1 -0
- package/dist/__tests__/listed-pages.test.js +88 -0
- package/dist/__tests__/listed-pages.test.js.map +1 -0
- package/dist/__tests__/oauth.test.js +50 -1
- package/dist/__tests__/oauth.test.js.map +1 -1
- package/dist/__tests__/privacy-page.test.js +33 -1
- package/dist/__tests__/privacy-page.test.js.map +1 -1
- package/dist/__tests__/tenant.test.js +72 -9
- package/dist/__tests__/tenant.test.js.map +1 -1
- package/dist/index.js +99 -15
- package/dist/index.js.map +1 -1
- package/dist/legal.d.ts +31 -0
- package/dist/legal.d.ts.map +1 -1
- package/dist/legal.js +138 -0
- package/dist/legal.js.map +1 -1
- package/dist/listed-pages.d.ts +24 -0
- package/dist/listed-pages.d.ts.map +1 -0
- package/dist/listed-pages.js +65 -0
- package/dist/listed-pages.js.map +1 -0
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +26 -3
- package/dist/oauth.js.map +1 -1
- package/dist/tenant.d.ts +11 -0
- package/dist/tenant.d.ts.map +1 -1
- package/dist/tenant.js +43 -2
- package/dist/tenant.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listed-pages.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/listed-pages.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// The listed app's merchant-facing pages.
|
|
2
|
+
//
|
|
3
|
+
// These went out wrong once, in the way that is hardest to notice from the
|
|
4
|
+
// code: the server ran the SINGLE-shop status page for an app that has no shop,
|
|
5
|
+
// so the address Shopify opens after an install said "This store is not
|
|
6
|
+
// connected yet · Connect " — an empty shop name, behind a link that answered
|
|
7
|
+
// 400. What is pinned here is therefore what the pages SAY, not that they
|
|
8
|
+
// render: that the connected page names the MERCHANT's store rather than our
|
|
9
|
+
// product, and that it carries the connector address, which is the only thing
|
|
10
|
+
// on it a merchant can act on.
|
|
11
|
+
import { describe, expect, test } from 'vitest';
|
|
12
|
+
import { connectPage, connectedPage, connectorUrl, listedLandingPage, } from '../listed-pages.js';
|
|
13
|
+
const ctx = {
|
|
14
|
+
appUrl: 'https://storefront.curless.ai',
|
|
15
|
+
productName: 'Curless Storefront',
|
|
16
|
+
};
|
|
17
|
+
const snap = (over = {}) => ({
|
|
18
|
+
products: [{ handle: 'a' }, { handle: 'b' }],
|
|
19
|
+
loadedAt: new Date('2026-09-10T12:34:56Z'),
|
|
20
|
+
discounts: [],
|
|
21
|
+
...over,
|
|
22
|
+
});
|
|
23
|
+
describe('connectorUrl', () => {
|
|
24
|
+
test('is the shop-scoped MCP path under the app URL', () => {
|
|
25
|
+
expect(connectorUrl(ctx, 'curless-demo.myshopify.com')).toBe('https://storefront.curless.ai/s/curless-demo.myshopify.com/mcp');
|
|
26
|
+
});
|
|
27
|
+
test('a trailing slash on the app URL does not double up', () => {
|
|
28
|
+
expect(connectorUrl({ ...ctx, appUrl: 'https://storefront.curless.ai/' }, 'x.myshopify.com')).toBe('https://storefront.curless.ai/s/x.myshopify.com/mcp');
|
|
29
|
+
});
|
|
30
|
+
test('lowercases the shop — the path resolver keys on it', () => {
|
|
31
|
+
expect(connectorUrl(ctx, 'Curless-Demo.MyShopify.com')).toContain('/s/curless-demo.myshopify.com/mcp');
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
describe('connectedPage', () => {
|
|
35
|
+
const html = connectedPage(ctx, 'curless-demo.myshopify.com', snap());
|
|
36
|
+
test('names the MERCHANT store, not our product', () => {
|
|
37
|
+
expect(html).toContain('curless-demo.myshopify.com');
|
|
38
|
+
// The bug this replaces: the callback congratulated the merchant on
|
|
39
|
+
// "Curless Storefront is connected" — our own name, on their install.
|
|
40
|
+
expect(html).not.toContain('Curless Storefront is connected');
|
|
41
|
+
});
|
|
42
|
+
test('carries the connector address — the one thing the merchant must copy', () => {
|
|
43
|
+
expect(html).toContain('https://storefront.curless.ai/s/curless-demo.myshopify.com/mcp');
|
|
44
|
+
});
|
|
45
|
+
test('reports the catalogue that was actually read', () => {
|
|
46
|
+
expect(html).toContain('2 products readable');
|
|
47
|
+
expect(html).toContain('2026-09-10 12:34');
|
|
48
|
+
});
|
|
49
|
+
test('a store whose last sync failed is told so, and still gets the address', () => {
|
|
50
|
+
const failed = connectedPage(ctx, 'x.myshopify.com', snap({ lastError: 'HTTP 401' }));
|
|
51
|
+
expect(failed).toContain('HTTP 401');
|
|
52
|
+
expect(failed).toContain('/s/x.myshopify.com/mcp');
|
|
53
|
+
});
|
|
54
|
+
test('a catalogue that has never loaded says nothing about when it synced', () => {
|
|
55
|
+
expect(connectedPage(ctx, 'x.myshopify.com', snap({ products: [], loadedAt: null }))).not.toContain('last synced');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe('connectPage', () => {
|
|
59
|
+
test('offers the install for THAT shop', () => {
|
|
60
|
+
expect(connectPage('curless-demo.myshopify.com')).toContain('href="/auth/install?shop=curless-demo.myshopify.com"');
|
|
61
|
+
});
|
|
62
|
+
test('is not the empty-shop page that shipped', () => {
|
|
63
|
+
// The regression, exactly: a button reading "Connect " with nothing after it.
|
|
64
|
+
expect(connectPage('curless-demo.myshopify.com')).not.toContain('Connect </a>');
|
|
65
|
+
});
|
|
66
|
+
test('a shop with markup in it cannot break out of the link or the text', () => {
|
|
67
|
+
const html = connectPage('evil".myshopify.com');
|
|
68
|
+
expect(html).not.toContain('evil".myshopify.com');
|
|
69
|
+
expect(html).toContain('evil".myshopify.com');
|
|
70
|
+
expect(html).toContain('shop=evil%22.myshopify.com');
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe('listedLandingPage', () => {
|
|
74
|
+
const html = listedLandingPage(ctx);
|
|
75
|
+
test('says what the app is without naming any merchant', () => {
|
|
76
|
+
expect(html).toContain('Curless Storefront');
|
|
77
|
+
expect(html).not.toContain('myshopify.com');
|
|
78
|
+
});
|
|
79
|
+
test('links the four pages a reviewer looks for', () => {
|
|
80
|
+
for (const href of ['/docs', '/privacy', '/terms', '/security']) {
|
|
81
|
+
expect(html).toContain(`href="${href}"`);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
test('says it is read-only, which is the listing claim', () => {
|
|
85
|
+
expect(html).toContain('read-only');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
//# sourceMappingURL=listed-pages.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listed-pages.test.js","sourceRoot":"","sources":["../../src/__tests__/listed-pages.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// which is exactly why they need tests rather than a careful reading.
|
|
4
4
|
import { createHmac } from 'node:crypto';
|
|
5
5
|
import { describe, expect, test, vi } from 'vitest';
|
|
6
|
-
import { exchangeCode, installUrl, isShopDomain, makeState, verifyCallbackHmac, verifyState, } from '../oauth.js';
|
|
6
|
+
import { exchangeCode, installUrl, isShopDomain, makeState, refreshAccessToken, verifyCallbackHmac, verifyState, } from '../oauth.js';
|
|
7
7
|
const CONFIG = {
|
|
8
8
|
clientId: 'cid',
|
|
9
9
|
clientSecret: 'shhh',
|
|
@@ -108,5 +108,54 @@ describe('code exchange', () => {
|
|
|
108
108
|
await expect(exchangeCode(CONFIG, 'bad', f)).rejects.toThrow(/HTTP 400/);
|
|
109
109
|
await expect(exchangeCode(CONFIG, 'bad', f)).rejects.not.toThrow(/reflected/);
|
|
110
110
|
});
|
|
111
|
+
test('posts to the SHOP in the config, not to some default', async () => {
|
|
112
|
+
const f = vi.fn(async () => new Response(JSON.stringify({ access_token: 'shpat_x', scope: 'read_products' }), {
|
|
113
|
+
status: 200,
|
|
114
|
+
headers: { 'content-type': 'application/json' },
|
|
115
|
+
}));
|
|
116
|
+
await exchangeCode({ ...CONFIG, shopDomain: 'other.myshopify.com' }, 'code', f);
|
|
117
|
+
expect(vi.mocked(f).mock.calls[0]?.[0]).toBe('https://other.myshopify.com/admin/oauth/access_token');
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
// ⚠️ THE LISTED APP HAS NO SHOP OF ITS OWN — its module-level config holds `''`,
|
|
121
|
+
// because the shop is a property of the REQUEST, not of the config. Every call
|
|
122
|
+
// site therefore has to spread the real shop in, and on 2026-09-10 the callback
|
|
123
|
+
// did not: it built `https:///admin/oauth/access_token`, undici answered
|
|
124
|
+
// `fetch failed`, and a real install died looking exactly like a network fault.
|
|
125
|
+
// An hour went into DNS and container egress before anyone looked at the host.
|
|
126
|
+
//
|
|
127
|
+
// These are the tests that would have caught it. Note that NONE of the 27 that
|
|
128
|
+
// existed did: every one of them passed a config with a shop already in it.
|
|
129
|
+
describe('an empty shop is refused out loud, not fetched', () => {
|
|
130
|
+
const NO_SHOP = { ...CONFIG, shopDomain: '' };
|
|
131
|
+
test('exchangeCode throws before it fetches anything', async () => {
|
|
132
|
+
const f = vi.fn();
|
|
133
|
+
await expect(exchangeCode(NO_SHOP, 'code', f)).rejects.toThrow(/no shop to talk to/);
|
|
134
|
+
// The point of failing early: nothing was sent to `https:///…`.
|
|
135
|
+
expect(vi.mocked(f)).not.toHaveBeenCalled();
|
|
136
|
+
});
|
|
137
|
+
test('refreshAccessToken throws before it fetches anything', async () => {
|
|
138
|
+
const f = vi.fn();
|
|
139
|
+
await expect(refreshAccessToken(NO_SHOP, 'rt', f)).rejects.toThrow(/no shop to talk to/);
|
|
140
|
+
expect(vi.mocked(f)).not.toHaveBeenCalled();
|
|
141
|
+
});
|
|
142
|
+
test('installUrl throws rather than returning https:///admin/oauth/authorize', () => {
|
|
143
|
+
expect(() => installUrl(NO_SHOP, 'st.at')).toThrow(/no shop to talk to/);
|
|
144
|
+
});
|
|
145
|
+
test('the error names the empty value, so the log says what is missing', async () => {
|
|
146
|
+
await expect(exchangeCode(NO_SHOP, 'code')).rejects.toThrow(/shopDomain is ""/);
|
|
147
|
+
});
|
|
148
|
+
test('a lookalike host is refused by the same door', async () => {
|
|
149
|
+
const evil = { ...CONFIG, shopDomain: 'shop.myshopify.com.evil.com' };
|
|
150
|
+
await expect(exchangeCode(evil, 'code')).rejects.toThrow(/no shop to talk to/);
|
|
151
|
+
});
|
|
152
|
+
test('spreading the request shop over the listed config is what works', async () => {
|
|
153
|
+
const f = vi.fn(async () => new Response(JSON.stringify({ access_token: 'shpat_x', scope: 'read_products' }), {
|
|
154
|
+
status: 200,
|
|
155
|
+
headers: { 'content-type': 'application/json' },
|
|
156
|
+
}));
|
|
157
|
+
await exchangeCode({ ...NO_SHOP, shopDomain: 'curless-demo.myshopify.com' }, 'code', f);
|
|
158
|
+
expect(vi.mocked(f).mock.calls[0]?.[0]).toBe('https://curless-demo.myshopify.com/admin/oauth/access_token');
|
|
159
|
+
});
|
|
111
160
|
});
|
|
112
161
|
//# sourceMappingURL=oauth.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.test.js","sourceRoot":"","sources":["../../src/__tests__/oauth.test.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"oauth.test.js","sourceRoot":"","sources":["../../src/__tests__/oauth.test.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,iFAAiF;AACjF,sEAAsE;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACpD,OAAO,EAEL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,SAAS,EACT,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,GACZ,MAAM,aAAa,CAAC;AAErB,MAAM,MAAM,GAAgB;IAC1B,QAAQ,EAAE,KAAK;IACf,YAAY,EAAE,MAAM;IACpB,UAAU,EAAE,2BAA2B;IACvC,MAAM,EAAE,8BAA8B;IACtC,MAAM,EAAE,2BAA2B;CACpC,CAAC;AAEF,0EAA0E;AAC1E,gDAAgD;AAChD,MAAM,WAAW,GAAG,CAAC,MAAc,EAAE,KAA6B,EAAU,EAAE;IAC5E,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;SAC7B,IAAI,EAAE;SACN,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAW,CAAC,EAAE,CAAC,CAAC;IAChE,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAChF,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC;AAC5C,CAAC,CAAC;AAEF,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,IAAI,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,YAAY,CAAC,2BAA2B,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7D,sEAAsE;QACtE,MAAM,CAAC,YAAY,CAAC,oCAAoC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvE,MAAM,CAAC,YAAY,CAAC,oCAAoC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvE,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;IAEnF,IAAI,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACvC,MAAM,CAAC,kBAAkB,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,kEAAkE;IAClE,IAAI,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACxC,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5F,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,kBAAkB,CAAC,MAAM,EAAE,WAAW,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,kBAAkB,CAAC,MAAM,EAAE,gCAAgC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;IAEH,0EAA0E;IAC1E,6EAA6E;IAC7E,+EAA+E;IAC/E,iBAAiB;IACjB,IAAI,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC7D,MAAM,CAAC,kBAAkB,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,IAAI,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACtC,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC/C,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrD,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrE,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,0EAA0E;IAC1E,6EAA6E;IAC7E,IAAI,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAChC,MAAM,GAAG,GAAG,iBAAiB,CAAC;QAC9B,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5D,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAClD,MAAM,GAAG,GAAG,iBAAiB,CAAC;QAC9B,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,GAAG,GAAG,EAAE,GAAG,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrF,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,IAAI,CAAC,gEAAgE,EAAE,GAAG,EAAE;QAC1E,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QACnD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QACpD,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QACxE,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;QAChG,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpD,6EAA6E;QAC7E,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,IAAI,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;QACnE,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,CACb,KAAK,IAAI,EAAE,CACT,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,EAAE;YAChF,MAAM,EAAE,GAAG;YACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;SAChD,CAAC,CACsB,CAAC;QAC7B,qEAAqE;QACrE,MAAM,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;YAC7D,WAAW,EAAE,SAAS;YACtB,KAAK,EAAE,eAAe;SACvB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,CACb,KAAK,IAAI,EAAE,CAAC,IAAI,QAAQ,CAAC,kCAAkC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CACnD,CAAC;QAC7B,MAAM,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACzE,MAAM,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,CACb,KAAK,IAAI,EAAE,CACT,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,EAAE;YAChF,MAAM,EAAE,GAAG;YACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;SAChD,CAAC,CACsB,CAAC;QAC7B,MAAM,YAAY,CAAC,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,qBAAqB,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QAChF,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAC1C,sDAAsD,CACvD,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,iFAAiF;AACjF,+EAA+E;AAC/E,gFAAgF;AAChF,yEAAyE;AACzE,gFAAgF;AAChF,+EAA+E;AAC/E,EAAE;AACF,+EAA+E;AAC/E,4EAA4E;AAC5E,QAAQ,CAAC,gDAAgD,EAAE,GAAG,EAAE;IAC9D,MAAM,OAAO,GAAgB,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;IAE3D,IAAI,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,EAA6B,CAAC;QAC7C,MAAM,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;QACrF,gEAAgE;QAChE,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,EAA6B,CAAC;QAC7C,MAAM,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;QACzF,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,wEAAwE,EAAE,GAAG,EAAE;QAClF,MAAM,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAClF,MAAM,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAClF,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,IAAI,GAAG,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,6BAA6B,EAAE,CAAC;QACtE,MAAM,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACjF,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,CACb,KAAK,IAAI,EAAE,CACT,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,EAAE;YAChF,MAAM,EAAE,GAAG;YACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;SAChD,CAAC,CACsB,CAAC;QAC7B,MAAM,YAAY,CAAC,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,4BAA4B,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QACxF,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAC1C,6DAA6D,CAC9D,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -11,7 +11,7 @@ import { readFileSync } from 'node:fs';
|
|
|
11
11
|
import { dirname, join } from 'node:path';
|
|
12
12
|
import { fileURLToPath } from 'node:url';
|
|
13
13
|
import { describe, expect, test } from 'vitest';
|
|
14
|
-
import { ORDER_FIELDS, docsPage, privacyPage } from '../legal.js';
|
|
14
|
+
import { ORDER_FIELDS, docsPage, merchantTermsPage, privacyPage, securityPage, } from '../legal.js';
|
|
15
15
|
const CTX = {
|
|
16
16
|
merchantName: 'Moody Lenses',
|
|
17
17
|
storefront: 'https://moodylenses.com',
|
|
@@ -126,6 +126,38 @@ describe('the listed app has no merchant to name', () => {
|
|
|
126
126
|
expect(ours, `points a shop-policy link at us: ${ours.join(', ')}`).toHaveLength(0);
|
|
127
127
|
}
|
|
128
128
|
});
|
|
129
|
+
// ⚠️ /terms WAS THE SAME 404, found only after the privacy page was fixed:
|
|
130
|
+
// the redirect lives in index.ts, not in legal.ts, so fixing the pages left
|
|
131
|
+
// it pointing at `<our own app url>/policies/terms-of-service`.
|
|
132
|
+
//
|
|
133
|
+
// These two pages are also the answer to two questions on Shopify's
|
|
134
|
+
// protected-customer-data questionnaire — "do you have a data protection
|
|
135
|
+
// agreement with your merchants" and "do you have a data loss prevention
|
|
136
|
+
// strategy" — which were answered No on 2026-09-10 because nothing was
|
|
137
|
+
// written down, not because the controls were missing.
|
|
138
|
+
test('the listed app has merchant terms and a written data-protection page', () => {
|
|
139
|
+
const terms = merchantTermsPage(multi);
|
|
140
|
+
expect(terms).toContain('data-processing agreement');
|
|
141
|
+
expect(terms).toContain('processor');
|
|
142
|
+
const sec = securityPage(multi);
|
|
143
|
+
expect(sec).toContain('data loss');
|
|
144
|
+
// The strongest claim, and the one the code actually backs.
|
|
145
|
+
expect(sec).toContain('no shopper record');
|
|
146
|
+
});
|
|
147
|
+
test('neither new page links a /policies path on our own domain', () => {
|
|
148
|
+
for (const page of [merchantTermsPage(multi), securityPage(multi)]) {
|
|
149
|
+
const ours = page.match(/https:\/\/[^"']*curless[^"']*\/policies\/[a-z-]+/g) ?? [];
|
|
150
|
+
expect(ours).toHaveLength(0);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
// ⚠️ Nothing here may promise more than the code keeps. These two were the
|
|
154
|
+
// tempting over-claims — an encrypted off-site backup we do not have, and a
|
|
155
|
+
// certification we have not been through.
|
|
156
|
+
test('claims nothing we cannot point at code for', () => {
|
|
157
|
+
const sec = securityPage(multi);
|
|
158
|
+
expect(sec).toContain('no third-party security certification');
|
|
159
|
+
expect(sec).toContain('keeps no shopper data');
|
|
160
|
+
});
|
|
129
161
|
// A branded deployment must keep its links — the fix must not cost moody and
|
|
130
162
|
// elspet the merchant policy links they are supposed to carry.
|
|
131
163
|
test('a branded deployment still links the merchant’s own policies', () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"privacy-page.test.js","sourceRoot":"","sources":["../../src/__tests__/privacy-page.test.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,8EAA8E;AAC9E,+EAA+E;AAC/E,iFAAiF;AACjF,WAAW;AACX,EAAE;AACF,iFAAiF;AACjF,6EAA6E;AAC7E,+EAA+E;AAE/E,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,
|
|
1
|
+
{"version":3,"file":"privacy-page.test.js","sourceRoot":"","sources":["../../src/__tests__/privacy-page.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import { describe, expect, test } from 'vitest';
|
|
2
2
|
import { resolveStorefront } from '../config.js';
|
|
3
3
|
import { explainNoTenant, multiShopResolver, singleShopResolver } from '../tenant.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const fakeCatalog = (tag, opts = {}) => {
|
|
5
|
+
const snap = { products: [], loadedAt: opts.loaded ? new Date() : null };
|
|
6
|
+
return {
|
|
7
|
+
tag,
|
|
8
|
+
get current() {
|
|
9
|
+
return snap;
|
|
10
|
+
},
|
|
11
|
+
refresh: async () => {
|
|
12
|
+
opts.refreshes?.push(tag);
|
|
13
|
+
if (opts.loadTakes)
|
|
14
|
+
await new Promise((r) => setTimeout(r, opts.loadTakes));
|
|
15
|
+
snap.products = [{ handle: 'loaded-by-the-wait' }];
|
|
16
|
+
snap.loadedAt = new Date();
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
};
|
|
8
20
|
const token = (shopDomain = 'rqxbft-fw.myshopify.com') => ({
|
|
9
21
|
shopDomain,
|
|
10
22
|
accessToken: 'x',
|
|
@@ -19,22 +31,23 @@ const single = (opts) => singleShopResolver({
|
|
|
19
31
|
urls: { appUrl: 'https://shopify.curless.ai/moody' },
|
|
20
32
|
legal: {},
|
|
21
33
|
}),
|
|
22
|
-
catalog: fakeCatalog('single'),
|
|
34
|
+
catalog: fakeCatalog('single', { loaded: true }),
|
|
23
35
|
tokens: { get: async () => (opts.installed ? token() : null) },
|
|
24
36
|
});
|
|
25
|
-
const multi = (installed) => {
|
|
37
|
+
const multi = (installed, opts = {}) => {
|
|
26
38
|
const started = [];
|
|
39
|
+
const refreshes = [];
|
|
27
40
|
const registry = {
|
|
28
41
|
for: (s) => {
|
|
29
42
|
started.push(s);
|
|
30
|
-
return fakeCatalog(s);
|
|
43
|
+
return fakeCatalog(s, { loaded: !opts.cold, loadTakes: opts.loadTakes, refreshes });
|
|
31
44
|
},
|
|
32
45
|
};
|
|
33
46
|
const tokens = {
|
|
34
47
|
get: async (s) => (installed.includes(s) ? token(s) : null),
|
|
35
48
|
};
|
|
36
|
-
const resolve = multiShopResolver({ registry, tokens });
|
|
37
|
-
return { resolve, started };
|
|
49
|
+
const resolve = multiShopResolver({ registry, tokens, coldStartWaitMs: 50 });
|
|
50
|
+
return { resolve, started, refreshes };
|
|
38
51
|
};
|
|
39
52
|
describe('branded deployment', () => {
|
|
40
53
|
test('a request naming nothing gets the one shop it serves', async () => {
|
|
@@ -102,4 +115,54 @@ describe('what the buyer is told', () => {
|
|
|
102
115
|
expect(explainNoTenant({ reason: 'not-installed', shop: 'a.myshopify.com' })).toMatch(/has not installed/i);
|
|
103
116
|
});
|
|
104
117
|
});
|
|
118
|
+
// ⚠️ THE FIRST QUESTION EVER ASKED OF A SHOP.
|
|
119
|
+
//
|
|
120
|
+
// `registry.for` creates the catalogue and starts its first load; the resolver
|
|
121
|
+
// used to hand that empty catalogue straight to the tool, which answered "the
|
|
122
|
+
// catalog is not loaded yet". Verified in production on 2026-09-10, one minute
|
|
123
|
+
// after a real install on the dev store: ask once, get the error card; ask
|
|
124
|
+
// again, get 15 products. A Shopify reviewer's step 4 is exactly that first ask.
|
|
125
|
+
describe('a catalogue that has never loaded', () => {
|
|
126
|
+
test('is waited for, so the first request answers with products', async () => {
|
|
127
|
+
const { resolve } = multi(['rqxbft-fw.myshopify.com'], { cold: true });
|
|
128
|
+
const r = await resolve('rqxbft-fw.myshopify.com');
|
|
129
|
+
expect(r.ok).toBe(true);
|
|
130
|
+
expect(r.ok && r.tenant.catalog.current.loadedAt).not.toBeNull();
|
|
131
|
+
expect(r.ok && r.tenant.catalog.current.products).toHaveLength(1);
|
|
132
|
+
});
|
|
133
|
+
test('a WARM catalogue is not refreshed on the way past', async () => {
|
|
134
|
+
const { resolve, refreshes } = multi(['rqxbft-fw.myshopify.com']);
|
|
135
|
+
await resolve('rqxbft-fw.myshopify.com');
|
|
136
|
+
expect(refreshes).toEqual([]);
|
|
137
|
+
});
|
|
138
|
+
test('the wait is BOUNDED — a load that hangs still answers', async () => {
|
|
139
|
+
// Nothing between the catalogue and Shopify sets a timeout, so an unbounded
|
|
140
|
+
// await here would hand a buyer's request to whatever Shopify does with it.
|
|
141
|
+
const { resolve } = multi(['rqxbft-fw.myshopify.com'], { cold: true, loadTakes: 5_000 });
|
|
142
|
+
const started = Date.now();
|
|
143
|
+
const r = await resolve('rqxbft-fw.myshopify.com');
|
|
144
|
+
expect(Date.now() - started).toBeLessThan(1_000);
|
|
145
|
+
// Answered anyway, with the empty snapshot — the old behaviour, but only
|
|
146
|
+
// after actually waiting for the load rather than instead of waiting.
|
|
147
|
+
expect(r.ok && r.tenant.catalog.current.loadedAt).toBeNull();
|
|
148
|
+
});
|
|
149
|
+
test('the branded resolver gets the same wait', async () => {
|
|
150
|
+
const refreshes = [];
|
|
151
|
+
const resolve = singleShopResolver({
|
|
152
|
+
store: resolveStorefront({
|
|
153
|
+
id: 'moody',
|
|
154
|
+
server: { name: 'moody', title: 'Moody' },
|
|
155
|
+
shopify: { mode: 'env', shopDomain: 'rqxbft-fw.myshopify.com' },
|
|
156
|
+
urls: { appUrl: 'https://shopify.curless.ai/moody' },
|
|
157
|
+
legal: {},
|
|
158
|
+
}),
|
|
159
|
+
catalog: fakeCatalog('cold', { refreshes }),
|
|
160
|
+
tokens: { get: async () => token() },
|
|
161
|
+
coldStartWaitMs: 50,
|
|
162
|
+
});
|
|
163
|
+
const r = await resolve();
|
|
164
|
+
expect(refreshes).toEqual(['cold']);
|
|
165
|
+
expect(r.ok && r.tenant.catalog.current.loadedAt).not.toBeNull();
|
|
166
|
+
});
|
|
167
|
+
});
|
|
105
168
|
//# sourceMappingURL=tenant.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tenant.test.js","sourceRoot":"","sources":["../../src/__tests__/tenant.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"tenant.test.js","sourceRoot":"","sources":["../../src/__tests__/tenant.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -54,7 +54,8 @@ import { Catalog } from './catalog.js';
|
|
|
54
54
|
import { challengeFor } from './challenge.js';
|
|
55
55
|
import { resolveStorefront } from './config.js';
|
|
56
56
|
import { credentialsRejected, needsAuth } from './connection.js';
|
|
57
|
-
import { docsPage, privacyPage } from './legal.js';
|
|
57
|
+
import { docsPage, merchantTermsPage, privacyPage, securityPage, } from './legal.js';
|
|
58
|
+
import { connectPage, connectedPage, escapeHtml, listedLandingPage, } from './listed-pages.js';
|
|
58
59
|
import { loadStorefrontConfig } from './load-config.js';
|
|
59
60
|
import { MultiTokenStore, isStorableShop } from './multi-token-store.js';
|
|
60
61
|
import { exchangeCode, installUrl, isShopDomain, makeState, verifyCallbackHmac, verifyState, } from './oauth.js';
|
|
@@ -271,6 +272,10 @@ const brandAsset = (rel) => {
|
|
|
271
272
|
const ICON_PNG = brandAsset(STORE.config.server.iconFile);
|
|
272
273
|
const LOGO_PNG = brandAsset(STORE.config.server.logoFile);
|
|
273
274
|
const MULTI = STORE.config.shopify.mode === 'oauth-install';
|
|
275
|
+
// What the listed app's own pages say it is. Its `server.title` is OUR product's
|
|
276
|
+
// name — the branded deployments put a merchant's there, which is exactly the
|
|
277
|
+
// confusion the connected page must not repeat.
|
|
278
|
+
const LISTED = { appUrl: APP_URL, productName: MERCHANT_NAME };
|
|
274
279
|
/**
|
|
275
280
|
* The shop a request is about.
|
|
276
281
|
*
|
|
@@ -389,7 +394,23 @@ app.get('/docs', (c) => c.html(docsPage({ ...LEGAL, storefront: storefrontUrl()
|
|
|
389
394
|
// Curless was rejected over three times, the answer has to be pointed at this
|
|
390
395
|
// page; a store policy cannot answer it. See docs/moody-connector-listing.md.
|
|
391
396
|
app.get('/privacy', (c) => c.html(privacyPage({ ...LEGAL, storefront: storefrontUrl() })));
|
|
392
|
-
|
|
397
|
+
// ⚠️ TWO DIFFERENT THINGS, and the split is the same one the privacy page makes.
|
|
398
|
+
//
|
|
399
|
+
// A branded connector redirects to the MERCHANT's terms of sale: the shopper is
|
|
400
|
+
// buying from them, and a second set from us would be a second answer to one
|
|
401
|
+
// question. The listed app has no merchant to redirect to — that redirect
|
|
402
|
+
// resolved to `<our own app url>/policies/terms-of-service`, verified 404 in
|
|
403
|
+
// production on 2026-09-10 — and it is also the deployment that needs terms of
|
|
404
|
+
// its own: Shopify's protected-customer-data questionnaire asks whether we have
|
|
405
|
+
// a data-processing agreement with our merchants, and this page is it.
|
|
406
|
+
// The written data-protection strategy. Only where there are merchants to make
|
|
407
|
+
// it to — a branded connector's merchant has their own.
|
|
408
|
+
if (MULTI) {
|
|
409
|
+
app.get('/security', (c) => c.html(securityPage({ ...LEGAL, storefront: storefrontUrl() })));
|
|
410
|
+
}
|
|
411
|
+
app.get('/terms', (c) => MULTI
|
|
412
|
+
? c.html(merchantTermsPage({ ...LEGAL, storefront: storefrontUrl() }))
|
|
413
|
+
: c.redirect(`${storefrontUrl()}/policies/terms-of-service`, 301));
|
|
393
414
|
// Product images, re-served from OUR origin.
|
|
394
415
|
//
|
|
395
416
|
// The card is drawn in a sandboxed iframe whose CSP the host sets, and Claude's
|
|
@@ -468,6 +489,26 @@ app.get('/img', async (c) => {
|
|
|
468
489
|
}
|
|
469
490
|
});
|
|
470
491
|
app.get('/healthz', async (c) => {
|
|
492
|
+
// ⚠️ The listed app has no single shop, and answering as if it did made this
|
|
493
|
+
// endpoint report `not-installed` with an empty shop domain forever — a health
|
|
494
|
+
// check that is wrong in precisely the state it exists to detect.
|
|
495
|
+
//
|
|
496
|
+
// What can actually be wrong here is a token that stopped renewing: this is
|
|
497
|
+
// the first deployment whose tokens expire, and a refresh that has been
|
|
498
|
+
// failing shows up as a deadline in the past long before a merchant notices
|
|
499
|
+
// their assistant has gone quiet.
|
|
500
|
+
if (MULTI) {
|
|
501
|
+
const shops = await multiTokens.list();
|
|
502
|
+
const now = Date.now();
|
|
503
|
+
const stale = shops.filter((t) => t.expiresAt !== undefined && Date.parse(t.expiresAt) < now).length;
|
|
504
|
+
return c.json({
|
|
505
|
+
status: stale > 0 ? 'degraded' : 'ok',
|
|
506
|
+
mode: 'listed',
|
|
507
|
+
shops: shops.length,
|
|
508
|
+
staleTokens: stale,
|
|
509
|
+
residentCatalogs: registry.size,
|
|
510
|
+
});
|
|
511
|
+
}
|
|
471
512
|
const snap = catalog.current;
|
|
472
513
|
const connected = (await shopifyConfig()) !== null;
|
|
473
514
|
return c.json({
|
|
@@ -521,6 +562,44 @@ app.get('/', async (c) => {
|
|
|
521
562
|
<p class="muted">No sign-in, and no payment happens here — checkout is on
|
|
522
563
|
${escapeHtml(MERCHANT_NAME)}' own page. <a href="/docs">What this connector can reach</a>.</p>`);
|
|
523
564
|
}
|
|
565
|
+
// ⚠️ EVERYTHING BELOW ASSUMES ONE SHOP. `catalog`, `shopifyConfig()` and
|
|
566
|
+
// SHOP_DOMAIN are the branded deployment's single tenant. The listed app has
|
|
567
|
+
// no shop of its own, and running that code anyway is what put "Connect " —
|
|
568
|
+
// an empty shop name, behind a link that 400s — on the page a merchant lands
|
|
569
|
+
// on the moment they finish installing.
|
|
570
|
+
//
|
|
571
|
+
// Three states, decided by the shop in the query:
|
|
572
|
+
// • no shop — what this is. Nothing tenant-specific, and no list of
|
|
573
|
+
// who has installed it.
|
|
574
|
+
// • shop, no token — offer the install, and start it outright when the
|
|
575
|
+
// query is Shopify's own (HMAC and all), so a merchant
|
|
576
|
+
// who just clicked Install is not asked to click Connect.
|
|
577
|
+
// • shop + token — connected, and THE ADDRESS THEY NEED: nothing else in
|
|
578
|
+
// the product tells a merchant what to paste into
|
|
579
|
+
// ChatGPT or Claude.
|
|
580
|
+
if (MULTI) {
|
|
581
|
+
const named = (c.req.query('shop') ?? '').trim().toLowerCase();
|
|
582
|
+
if (!named)
|
|
583
|
+
return c.html(listedLandingPage(LISTED));
|
|
584
|
+
// The same allow-list the token store keys on. `shop` decides where the
|
|
585
|
+
// button below sends someone, so a lookalike domain must not reach it.
|
|
586
|
+
if (!isStorableShop(named))
|
|
587
|
+
return c.text('that is not a Shopify store domain', 400);
|
|
588
|
+
if (await multiTokens.get(named)) {
|
|
589
|
+
const cat = registry.for(named);
|
|
590
|
+
// First view after an install: the catalogue was created a moment ago and
|
|
591
|
+
// its boot fetch is still in flight. `refresh()` joins that one rather
|
|
592
|
+
// than starting a second, so this waits for the load instead of telling a
|
|
593
|
+
// merchant with a full store that it has no products.
|
|
594
|
+
if (cat.current.loadedAt === null)
|
|
595
|
+
await cat.refresh();
|
|
596
|
+
return c.html(connectedPage(LISTED, named, cat.current));
|
|
597
|
+
}
|
|
598
|
+
if (CLIENT_SECRET && verifyCallbackHmac(CLIENT_SECRET, new URL(c.req.url).search)) {
|
|
599
|
+
return c.redirect(installUrl({ ...oauth, shopDomain: named }, makeState(CLIENT_SECRET)));
|
|
600
|
+
}
|
|
601
|
+
return c.html(connectPage(named));
|
|
602
|
+
}
|
|
524
603
|
const snap = catalog.current;
|
|
525
604
|
const connected = (await shopifyConfig()) !== null;
|
|
526
605
|
// Shopify sends the merchant HERE after a legacy-flow install, with ?shop=…
|
|
@@ -680,7 +759,14 @@ app.get('/auth/callback', async (c) => {
|
|
|
680
759
|
if (!code)
|
|
681
760
|
return c.text('missing code', 400);
|
|
682
761
|
try {
|
|
683
|
-
|
|
762
|
+
// ⚠️ THE SHOP COMES FROM THE CALLBACK, NOT FROM THE CONFIG. `oauth` carries
|
|
763
|
+
// the BRANDED deployment's single shop; the listed app's is `''`, because
|
|
764
|
+
// its shop is whoever is installing. Passing `oauth` here built
|
|
765
|
+
// `https:///admin/oauth/access_token` and every install on the listed app
|
|
766
|
+
// died with `fetch failed` — the install route already spreads the shop in,
|
|
767
|
+
// and this one, three hundred lines away, did not. `shopEndpoint` now
|
|
768
|
+
// refuses an empty shop out loud so the next one cannot be silent.
|
|
769
|
+
const { accessToken, scope, refreshToken, expiresAt } = await exchangeCode({ ...oauth, shopDomain: shop }, code);
|
|
684
770
|
// Per-shop in the listed app, single-file in a branded one. The record is
|
|
685
771
|
// the same; where it lands is the whole difference between serving one
|
|
686
772
|
// merchant and serving many.
|
|
@@ -701,6 +787,16 @@ app.get('/auth/callback', async (c) => {
|
|
|
701
787
|
await tokens.set(record);
|
|
702
788
|
console.log(`[oauth] installed on ${shop} (scopes: ${scope})`);
|
|
703
789
|
// The catalog is empty until now; fill it before the merchant looks.
|
|
790
|
+
//
|
|
791
|
+
// ⚠️ THE RIGHT ONE. `catalog` is the branded deployment's single tenant, and
|
|
792
|
+
// in the listed app it belongs to no shop at all — so an install refreshed
|
|
793
|
+
// nothing, and then told the merchant that "Curless Storefront is
|
|
794
|
+
// connected", which is OUR product's name rather than their store's.
|
|
795
|
+
if (MULTI) {
|
|
796
|
+
const cat = registry.for(shop);
|
|
797
|
+
await cat.refresh();
|
|
798
|
+
return c.html(connectedPage(LISTED, shop.toLowerCase(), cat.current));
|
|
799
|
+
}
|
|
704
800
|
void catalog.refresh();
|
|
705
801
|
return c.html(`<!doctype html><meta charset="utf-8"><link rel="icon" href="./icon.png"><title>Connected</title>
|
|
706
802
|
<body style="font-family:system-ui;padding:40px;max-width:32rem">
|
|
@@ -734,18 +830,6 @@ app.get('/.well-known/openai-apps-challenge', (c) => {
|
|
|
734
830
|
return c.notFound();
|
|
735
831
|
return c.text(token);
|
|
736
832
|
});
|
|
737
|
-
// Anything interpolated into the page below comes from our own env, but escape
|
|
738
|
-
// it anyway: the day one of these becomes merchant-supplied, the page should not
|
|
739
|
-
// be the thing that has to be remembered.
|
|
740
|
-
const escapeHtml = (s) => s.replace(/[&<>"']/g, (ch) => ch === '&'
|
|
741
|
-
? '&'
|
|
742
|
-
: ch === '<'
|
|
743
|
-
? '<'
|
|
744
|
-
: ch === '>'
|
|
745
|
-
? '>'
|
|
746
|
-
: ch === '"'
|
|
747
|
-
? '"'
|
|
748
|
-
: ''');
|
|
749
833
|
// The MCP Apps card. Declared on the tools AND on their results — a host reads
|
|
750
834
|
// it from either — and served over resources/read. Version the URI: hosts cache
|
|
751
835
|
// by it, and a stale card is worse than no card because it looks current.
|