@tanqory/plugin-sdk 0.6.1 → 0.6.3
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/LICENSE +21 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +13 -4
- package/dist/client/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/manifest.schema.spec.d.ts +0 -18
- package/dist/manifest.schema.spec.d.ts.map +0 -1
- package/dist/manifest.schema.spec.js +0 -164
- package/dist/manifest.schema.spec.js.map +0 -1
- package/dist/manifest.spec.d.ts +0 -2
- package/dist/manifest.spec.d.ts.map +0 -1
- package/dist/manifest.spec.js +0 -193
- package/dist/manifest.spec.js.map +0 -1
- package/dist/webhooks.spec.d.ts +0 -2
- package/dist/webhooks.spec.d.ts.map +0 -1
- package/dist/webhooks.spec.js +0 -74
- package/dist/webhooks.spec.js.map +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tanqory Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,aAAa,EAAoB,MAAM,qBAAqB,CAAA;AAG7F;;;;;;GAMG;AAEH,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAA;AAE5D,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,gBAAgB,CAAA;IACxB,KAAK,EAAE,OAAO,CAAA;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,6BAA6B;IAC7B,KAAK,EAAE,aAAa,GAAG,IAAI,CAAA;IAC3B,kEAAkE;IAClE,GAAG,EAAE,WAAW,GAAG,IAAI,CAAA;IACvB,6EAA6E;IAC7E,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAA;IAC/B,sFAAsF;IACtF,KAAK,EAAE,CAAC,OAAO,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CAC5F;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IAC7F,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IAC3D,KAAK,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IAC5D,MAAM,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;CAC9C;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;CAC9B;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,eAAe,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,aAAa,EAAoB,MAAM,qBAAqB,CAAA;AAG7F;;;;;;GAMG;AAEH,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAA;AAE5D,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,gBAAgB,CAAA;IACxB,KAAK,EAAE,OAAO,CAAA;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,6BAA6B;IAC7B,KAAK,EAAE,aAAa,GAAG,IAAI,CAAA;IAC3B,kEAAkE;IAClE,GAAG,EAAE,WAAW,GAAG,IAAI,CAAA;IACvB,6EAA6E;IAC7E,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAA;IAC/B,sFAAsF;IACtF,KAAK,EAAE,CAAC,OAAO,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CAC5F;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IAC7F,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IAC3D,KAAK,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IAC5D,MAAM,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;CAC9C;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;CAC9B;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,eAAe,CAkFjF"}
|
package/dist/client/index.js
CHANGED
|
@@ -97,10 +97,19 @@ function useTanqoryApp(options = {}) {
|
|
|
97
97
|
error,
|
|
98
98
|
store,
|
|
99
99
|
api,
|
|
100
|
-
can: (scope) =>
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
can: (scope) => {
|
|
101
|
+
if (!store)
|
|
102
|
+
return false;
|
|
103
|
+
// The host already intersects the merchant's permissions with the app's
|
|
104
|
+
// grant for a marketplace guest (app-bridge ≥0.7), so `permissions` is
|
|
105
|
+
// the right thing to check on either path. `grantedScopes`, when present,
|
|
106
|
+
// is the grant itself — a scope outside it is refused even for a store
|
|
107
|
+
// owner, whose `'*'` wildcard otherwise made this return true for every
|
|
108
|
+
// scope regardless of what was granted.
|
|
109
|
+
if (store.grantedScopes && !store.grantedScopes.includes(scope))
|
|
110
|
+
return false;
|
|
111
|
+
return store.permissions.includes('*') || store.permissions.includes(scope);
|
|
112
|
+
},
|
|
104
113
|
toast: (variant, title, description) => bridgeRef.current?.post('TOAST', { variant, title, description }),
|
|
105
114
|
};
|
|
106
115
|
}
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":";;AA2EA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The JSON Schema and `parseAppManifest` must agree.
|
|
3
|
-
*
|
|
4
|
-
* `parseAppManifest` is hand-written on purpose — this package is installed by
|
|
5
|
-
* strangers, and a schema library would be one more dependency they are asked to
|
|
6
|
-
* trust. So the schema shipped for editors and agents cannot be derived from it;
|
|
7
|
-
* it is written alongside, and would drift the moment anyone changed one and not
|
|
8
|
-
* the other.
|
|
9
|
-
*
|
|
10
|
-
* This is what stops that. Every case runs through both, and the two must reach
|
|
11
|
-
* the same verdict. A rule added to one and forgotten in the other fails here
|
|
12
|
-
* rather than in someone's editor six weeks later, telling them a manifest is
|
|
13
|
-
* fine that submission will reject.
|
|
14
|
-
*
|
|
15
|
-
* `ajv` is a devDependency. Nothing here ships.
|
|
16
|
-
*/
|
|
17
|
-
export {};
|
|
18
|
-
//# sourceMappingURL=manifest.schema.spec.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.schema.spec.d.ts","sourceRoot":"","sources":["../src/manifest.schema.spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* The JSON Schema and `parseAppManifest` must agree.
|
|
4
|
-
*
|
|
5
|
-
* `parseAppManifest` is hand-written on purpose — this package is installed by
|
|
6
|
-
* strangers, and a schema library would be one more dependency they are asked to
|
|
7
|
-
* trust. So the schema shipped for editors and agents cannot be derived from it;
|
|
8
|
-
* it is written alongside, and would drift the moment anyone changed one and not
|
|
9
|
-
* the other.
|
|
10
|
-
*
|
|
11
|
-
* This is what stops that. Every case runs through both, and the two must reach
|
|
12
|
-
* the same verdict. A rule added to one and forgotten in the other fails here
|
|
13
|
-
* rather than in someone's editor six weeks later, telling them a manifest is
|
|
14
|
-
* fine that submission will reject.
|
|
15
|
-
*
|
|
16
|
-
* `ajv` is a devDependency. Nothing here ships.
|
|
17
|
-
*/
|
|
18
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
19
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
20
|
-
};
|
|
21
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
const node_fs_1 = require("node:fs");
|
|
23
|
-
const node_path_1 = require("node:path");
|
|
24
|
-
const ajv_1 = __importDefault(require("ajv"));
|
|
25
|
-
const vitest_1 = require("vitest");
|
|
26
|
-
const manifest_1 = require("./manifest");
|
|
27
|
-
const schema = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, '..', 'schema', 'tanqory-app.schema.json'), 'utf8'));
|
|
28
|
-
const ajv = new ajv_1.default({ allErrors: true, strict: false });
|
|
29
|
-
const validateWithSchema = ajv.compile(schema);
|
|
30
|
-
const VALID = {
|
|
31
|
-
handle: 'order-notifier',
|
|
32
|
-
name: 'Order Notifier',
|
|
33
|
-
appUrl: 'https://order-notifier.example.com',
|
|
34
|
-
devUrl: 'http://localhost:3000',
|
|
35
|
-
embedded: { enabled: true, path: '/embed' },
|
|
36
|
-
scopes: ['orders.view'],
|
|
37
|
-
webhooks: [{ topic: 'orders/create', path: '/hooks/orders' }],
|
|
38
|
-
};
|
|
39
|
-
/** What each side says about the same input. */
|
|
40
|
-
function verdicts(manifest) {
|
|
41
|
-
let validator = true;
|
|
42
|
-
try {
|
|
43
|
-
(0, manifest_1.parseAppManifest)(manifest);
|
|
44
|
-
}
|
|
45
|
-
catch {
|
|
46
|
-
validator = false;
|
|
47
|
-
}
|
|
48
|
-
return { schema: validateWithSchema(manifest), validator };
|
|
49
|
-
}
|
|
50
|
-
const cases = [
|
|
51
|
-
['the scaffold default', VALID, true],
|
|
52
|
-
['no devUrl', { ...VALID, devUrl: undefined }, true],
|
|
53
|
-
[
|
|
54
|
-
'a manifest with every optional field',
|
|
55
|
-
{
|
|
56
|
-
...VALID,
|
|
57
|
-
supportUrl: 'https://example.com/support',
|
|
58
|
-
privacyPolicyUrl: 'https://example.com/privacy',
|
|
59
|
-
appType: 'embed',
|
|
60
|
-
navigation: { placement: 'orders', items: [{ label: 'Settings', path: '/embed/settings' }] },
|
|
61
|
-
iframe: { minHeight: 600, width: 'full' },
|
|
62
|
-
},
|
|
63
|
-
true,
|
|
64
|
-
],
|
|
65
|
-
[
|
|
66
|
-
'notification capability with a dispatchUrl',
|
|
67
|
-
{
|
|
68
|
-
...VALID,
|
|
69
|
-
provides: ['notification-channel:sms'],
|
|
70
|
-
notifications: { dispatchUrl: 'https://example.com/dispatch' },
|
|
71
|
-
},
|
|
72
|
-
true,
|
|
73
|
-
],
|
|
74
|
-
['an uppercase handle', { ...VALID, handle: 'OrderNotifier' }, false],
|
|
75
|
-
['a handle with spaces', { ...VALID, handle: 'order notifier' }, false],
|
|
76
|
-
['a leading hyphen in the handle', { ...VALID, handle: '-order' }, false],
|
|
77
|
-
['no handle at all', { ...VALID, handle: undefined }, false],
|
|
78
|
-
['an empty name', { ...VALID, name: '' }, false],
|
|
79
|
-
['a plain http appUrl', { ...VALID, appUrl: 'http://example.com' }, false],
|
|
80
|
-
[
|
|
81
|
-
'a devUrl on plain http that is not localhost',
|
|
82
|
-
{ ...VALID, devUrl: 'http://example.com' },
|
|
83
|
-
false,
|
|
84
|
-
],
|
|
85
|
-
['scopes as a string', { ...VALID, scopes: 'orders.view' }, false],
|
|
86
|
-
['a duplicated scope', { ...VALID, scopes: ['orders.view', 'orders.view'] }, false],
|
|
87
|
-
[
|
|
88
|
-
'an embedded.path with no leading slash',
|
|
89
|
-
{ ...VALID, embedded: { enabled: true, path: 'embed' } },
|
|
90
|
-
false,
|
|
91
|
-
],
|
|
92
|
-
['embedded.enabled as a string', { ...VALID, embedded: { enabled: 'yes' } }, false],
|
|
93
|
-
[
|
|
94
|
-
'a webhook path with no leading slash',
|
|
95
|
-
{ ...VALID, webhooks: [{ topic: 'orders/create', path: 'hooks' }] },
|
|
96
|
-
false,
|
|
97
|
-
],
|
|
98
|
-
['a webhook with no topic', { ...VALID, webhooks: [{ path: '/hooks' }] }, false],
|
|
99
|
-
['appType outside the enum', { ...VALID, appType: 'popup' }, false],
|
|
100
|
-
['iframe.width outside the enum', { ...VALID, iframe: { width: 'wide' } }, false],
|
|
101
|
-
['a negative iframe.minHeight', { ...VALID, iframe: { minHeight: -1 } }, false],
|
|
102
|
-
['navigation with no placement', { ...VALID, navigation: { items: [] } }, false],
|
|
103
|
-
[
|
|
104
|
-
'a navigation item with no label',
|
|
105
|
-
{ ...VALID, navigation: { placement: 'orders', items: [{ path: '/embed' }] } },
|
|
106
|
-
false,
|
|
107
|
-
],
|
|
108
|
-
// The field that never existed but shipped in an early first-party manifest.
|
|
109
|
-
// Rejected by name so the fix is obvious rather than silently ignored.
|
|
110
|
-
['the legacy embeddedPath field', { ...VALID, embeddedPath: '/embed' }, false],
|
|
111
|
-
// Taking over a notification with nowhere for it to go.
|
|
112
|
-
[
|
|
113
|
-
'a notification capability with no dispatchUrl',
|
|
114
|
-
{ ...VALID, provides: ['notification-override:order_confirmation'] },
|
|
115
|
-
false,
|
|
116
|
-
],
|
|
117
|
-
[
|
|
118
|
-
'a dispatchUrl on localhost',
|
|
119
|
-
{
|
|
120
|
-
...VALID,
|
|
121
|
-
provides: ['notification-channel:sms'],
|
|
122
|
-
notifications: { dispatchUrl: 'http://localhost:3000/dispatch' },
|
|
123
|
-
},
|
|
124
|
-
false,
|
|
125
|
-
],
|
|
126
|
-
];
|
|
127
|
-
(0, vitest_1.describe)('the JSON Schema agrees with parseAppManifest', () => {
|
|
128
|
-
vitest_1.it.each(cases)('%s', (_label, manifest, expected) => {
|
|
129
|
-
const { schema: bySchema, validator: byValidator } = verdicts(manifest);
|
|
130
|
-
// The assertion that matters is that they agree. Comparing both to the
|
|
131
|
-
// expectation separately would let a case pass while the two disagreed
|
|
132
|
-
// about why.
|
|
133
|
-
(0, vitest_1.expect)({ schema: bySchema, validator: byValidator }).toEqual({
|
|
134
|
-
schema: expected,
|
|
135
|
-
validator: expected,
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
(0, vitest_1.it)('lists every field the type declares, so a new one cannot be forgotten', () => {
|
|
139
|
-
// `additionalProperties: false` means a field added to the type but not to
|
|
140
|
-
// the schema is rejected by an editor while submission accepts it — the
|
|
141
|
-
// drift that is hardest to spot, because the tool is wrong and the platform
|
|
142
|
-
// is right.
|
|
143
|
-
const declared = Object.keys(schema.properties).sort();
|
|
144
|
-
(0, vitest_1.expect)(declared).toEqual([
|
|
145
|
-
'$schema',
|
|
146
|
-
'appType',
|
|
147
|
-
'appUrl',
|
|
148
|
-
'devUrl',
|
|
149
|
-
'embedded',
|
|
150
|
-
'embeddedPath',
|
|
151
|
-
'handle',
|
|
152
|
-
'iframe',
|
|
153
|
-
'name',
|
|
154
|
-
'navigation',
|
|
155
|
-
'notifications',
|
|
156
|
-
'privacyPolicyUrl',
|
|
157
|
-
'provides',
|
|
158
|
-
'scopes',
|
|
159
|
-
'supportUrl',
|
|
160
|
-
'webhooks',
|
|
161
|
-
].sort());
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
//# sourceMappingURL=manifest.schema.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.schema.spec.js","sourceRoot":"","sources":["../src/manifest.schema.spec.ts"],"names":[],"mappings":"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"}
|
package/dist/manifest.spec.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.spec.d.ts","sourceRoot":"","sources":["../src/manifest.spec.ts"],"names":[],"mappings":""}
|
package/dist/manifest.spec.js
DELETED
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const vitest_1 = require("vitest");
|
|
4
|
-
const manifest_1 = require("./manifest");
|
|
5
|
-
const VALID = {
|
|
6
|
-
handle: 'order-notifier',
|
|
7
|
-
name: 'Order Notifier',
|
|
8
|
-
appUrl: 'https://order-notifier.example.com',
|
|
9
|
-
scopes: ['orders.view'],
|
|
10
|
-
};
|
|
11
|
-
/** Collect the problems without asserting on prose. */
|
|
12
|
-
function problemsOf(input) {
|
|
13
|
-
try {
|
|
14
|
-
(0, manifest_1.parseAppManifest)(input);
|
|
15
|
-
return [];
|
|
16
|
-
}
|
|
17
|
-
catch (error) {
|
|
18
|
-
return error instanceof manifest_1.ManifestError ? error.problems : [String(error)];
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
(0, vitest_1.describe)('parseAppManifest', () => {
|
|
22
|
-
(0, vitest_1.it)('accepts a minimal manifest', () => {
|
|
23
|
-
(0, vitest_1.expect)((0, manifest_1.parseAppManifest)(VALID)).toMatchObject({ handle: 'order-notifier' });
|
|
24
|
-
});
|
|
25
|
-
(0, vitest_1.it)('reports every problem at once, not just the first', () => {
|
|
26
|
-
// A validator that stops at the first error turns a five-field mistake into
|
|
27
|
-
// five round trips — and this is the first thing a new developer runs.
|
|
28
|
-
const problems = problemsOf({ handle: 'Not Valid', appUrl: 'ftp://x', scopes: 'nope' });
|
|
29
|
-
(0, vitest_1.expect)(problems.length).toBeGreaterThanOrEqual(4);
|
|
30
|
-
});
|
|
31
|
-
(0, vitest_1.describe)('handle', () => {
|
|
32
|
-
vitest_1.it.each([
|
|
33
|
-
['uppercase', 'OrderNotifier'],
|
|
34
|
-
['spaces', 'order notifier'],
|
|
35
|
-
['leading hyphen', '-order'],
|
|
36
|
-
['double hyphen', 'order--notifier'],
|
|
37
|
-
['empty', ''],
|
|
38
|
-
])('rejects %s', (_label, handle) => {
|
|
39
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, handle })).not.toEqual([]);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
(0, vitest_1.describe)('urls', () => {
|
|
43
|
-
(0, vitest_1.it)('requires https for appUrl', () => {
|
|
44
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, appUrl: 'http://example.com' })).not.toEqual([]);
|
|
45
|
-
});
|
|
46
|
-
(0, vitest_1.it)('allows http on localhost for devUrl only', () => {
|
|
47
|
-
// A merchant is never sent to devUrl, and demanding TLS on a laptop is
|
|
48
|
-
// friction with no payoff.
|
|
49
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, devUrl: 'http://localhost:3000' })).toEqual([]);
|
|
50
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, devUrl: 'http://evil.example.com' })).not.toEqual([]);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
(0, vitest_1.describe)('scopes', () => {
|
|
54
|
-
(0, vitest_1.it)('rejects a duplicate', () => {
|
|
55
|
-
// Silently de-duplicating would make the consent screen and the manifest
|
|
56
|
-
// disagree about what was asked for.
|
|
57
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, scopes: ['orders.view', 'orders.view'] })).not.toEqual([]);
|
|
58
|
-
});
|
|
59
|
-
(0, vitest_1.it)('accepts an empty list', () => {
|
|
60
|
-
// An app that only renders UI needs no scopes, and forcing one would push
|
|
61
|
-
// developers to request something they do not use.
|
|
62
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, scopes: [] })).toEqual([]);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
(0, vitest_1.describe)('webhooks', () => {
|
|
66
|
-
(0, vitest_1.it)('requires a topic and a rooted path', () => {
|
|
67
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, webhooks: [{ topic: '', path: 'no-slash' }] }).length).toBe(2);
|
|
68
|
-
});
|
|
69
|
-
(0, vitest_1.it)('accepts a well-formed subscription', () => {
|
|
70
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, webhooks: [{ topic: 'orders/create', path: '/hooks/orders' }] })).toEqual([]);
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
(0, vitest_1.it)('requires embedded.path to be rooted when present', () => {
|
|
74
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, embedded: { enabled: true, path: 'embed' } })).not.toEqual([]);
|
|
75
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, embedded: { enabled: true, path: '/embed' } })).toEqual([]);
|
|
76
|
-
});
|
|
77
|
-
(0, vitest_1.it)('does not throw on null or a non-object', () => {
|
|
78
|
-
(0, vitest_1.expect)(problemsOf(null)).not.toEqual([]);
|
|
79
|
-
(0, vitest_1.expect)(problemsOf('a string')).not.toEqual([]);
|
|
80
|
-
});
|
|
81
|
-
(0, vitest_1.describe)('manifest v2 — surface declaration', () => {
|
|
82
|
-
(0, vitest_1.it)('accepts a full v2 manifest', () => {
|
|
83
|
-
(0, vitest_1.expect)(problemsOf({
|
|
84
|
-
...VALID,
|
|
85
|
-
appType: 'embed',
|
|
86
|
-
navigation: {
|
|
87
|
-
placement: 'marketing',
|
|
88
|
-
items: [
|
|
89
|
-
{ label: 'Campaigns', path: '/campaigns' },
|
|
90
|
-
{ label: 'Automations', path: '/automations' },
|
|
91
|
-
],
|
|
92
|
-
},
|
|
93
|
-
iframe: { minHeight: 480, width: 'full' },
|
|
94
|
-
provides: ['marketing-suite'],
|
|
95
|
-
})).toEqual([]);
|
|
96
|
-
});
|
|
97
|
-
(0, vitest_1.it)('a v1 manifest with none of the new fields stays valid unchanged', () => {
|
|
98
|
-
(0, vitest_1.expect)(problemsOf(VALID)).toEqual([]);
|
|
99
|
-
});
|
|
100
|
-
(0, vitest_1.it)('rejects the legacy top-level embeddedPath by name', () => {
|
|
101
|
-
const problems = problemsOf({ ...VALID, embeddedPath: '/embed' });
|
|
102
|
-
(0, vitest_1.expect)(problems.join(' ')).toContain('embeddedPath');
|
|
103
|
-
(0, vitest_1.expect)(problems.join(' ')).toContain('embedded: { enabled: true');
|
|
104
|
-
});
|
|
105
|
-
(0, vitest_1.it)('rejects an unknown appType', () => {
|
|
106
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, appType: 'popup' })).not.toEqual([]);
|
|
107
|
-
});
|
|
108
|
-
(0, vitest_1.it)('navigation.placement must be a slug; membership is enforced at review, not here', () => {
|
|
109
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, navigation: { placement: 'Not A Slug' } })).not.toEqual([]);
|
|
110
|
-
// Shape-valid but not a known slot today — the SDK passes it so a new
|
|
111
|
-
// platform slot never requires an SDK release; review owns the list.
|
|
112
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, navigation: { placement: 'future_slot' } })).toEqual([]);
|
|
113
|
-
});
|
|
114
|
-
(0, vitest_1.it)('navigation items need a label and a rooted, traversal-free path', () => {
|
|
115
|
-
const problems = problemsOf({
|
|
116
|
-
...VALID,
|
|
117
|
-
navigation: { placement: 'marketing', items: [{ label: '', path: '/a/../b' }] },
|
|
118
|
-
});
|
|
119
|
-
(0, vitest_1.expect)(problems.length).toBe(2);
|
|
120
|
-
});
|
|
121
|
-
(0, vitest_1.it)('iframe constraints: positive minHeight, width from the pair', () => {
|
|
122
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, iframe: { minHeight: -1 } })).not.toEqual([]);
|
|
123
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, iframe: { width: 'wide' } })).not.toEqual([]);
|
|
124
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, iframe: { minHeight: 320, width: 'centered' } })).toEqual([]);
|
|
125
|
-
});
|
|
126
|
-
(0, vitest_1.it)('provides entries are slugs, no duplicates', () => {
|
|
127
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, provides: ['Marketing Suite'] })).not.toEqual([]);
|
|
128
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, provides: ['marketing-suite', 'marketing-suite'] })).not.toEqual([]);
|
|
129
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, provides: ['marketing-suite'] })).toEqual([]);
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
(0, vitest_1.describe)('notification capabilities', () => {
|
|
133
|
-
const NOTIFY = { dispatchUrl: 'https://hooks.example.com/tanqory/notifications' };
|
|
134
|
-
(0, vitest_1.it)('accepts an override and a channel alongside a plain capability', () => {
|
|
135
|
-
(0, vitest_1.expect)(problemsOf({
|
|
136
|
-
...VALID,
|
|
137
|
-
provides: [
|
|
138
|
-
'marketing-suite',
|
|
139
|
-
'notification-override:order_new',
|
|
140
|
-
'notification-channel:line',
|
|
141
|
-
],
|
|
142
|
-
notifications: NOTIFY,
|
|
143
|
-
})).toEqual([]);
|
|
144
|
-
});
|
|
145
|
-
(0, vitest_1.it)('passes an unknown type or channel name — that vocabulary is the platform’s', () => {
|
|
146
|
-
// Shape-valid but not in today's registry. The SDK lets it through for
|
|
147
|
-
// the same reason it lets an unknown nav placement through: the list
|
|
148
|
-
// lives server-side and must grow without an SDK release.
|
|
149
|
-
(0, vitest_1.expect)(problemsOf({
|
|
150
|
-
...VALID,
|
|
151
|
-
provides: ['notification-override:some_future_type'],
|
|
152
|
-
notifications: NOTIFY,
|
|
153
|
-
})).toEqual([]);
|
|
154
|
-
});
|
|
155
|
-
(0, vitest_1.it)('rejects an unknown capability family', () => {
|
|
156
|
-
const problems = problemsOf({
|
|
157
|
-
...VALID,
|
|
158
|
-
provides: ['notification-takeover:order_new'],
|
|
159
|
-
notifications: NOTIFY,
|
|
160
|
-
});
|
|
161
|
-
(0, vitest_1.expect)(problems.join(' ')).toContain('notification-takeover');
|
|
162
|
-
(0, vitest_1.expect)(problems.join(' ')).toContain('notification-override:<name>');
|
|
163
|
-
});
|
|
164
|
-
vitest_1.it.each([
|
|
165
|
-
['an empty name', 'notification-override:'],
|
|
166
|
-
['an uppercase name', 'notification-channel:LINE'],
|
|
167
|
-
['a spaced name', 'notification-override:order new'],
|
|
168
|
-
['a second colon', 'notification-override:order:new'],
|
|
169
|
-
])('rejects %s', (_label, capability) => {
|
|
170
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, provides: [capability], notifications: NOTIFY })).not.toEqual([]);
|
|
171
|
-
});
|
|
172
|
-
(0, vitest_1.it)('requires a dispatchUrl once a notification capability is declared', () => {
|
|
173
|
-
// A takeover with nowhere to deliver drops every message it claimed —
|
|
174
|
-
// the one failure mode a merchant would never see in the UI.
|
|
175
|
-
const problems = problemsOf({ ...VALID, provides: ['notification-override:order_new'] });
|
|
176
|
-
(0, vitest_1.expect)(problems.join(' ')).toContain('notifications.dispatchUrl');
|
|
177
|
-
});
|
|
178
|
-
(0, vitest_1.it)('does not require a dispatchUrl for a plain capability', () => {
|
|
179
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, provides: ['marketing-suite'] })).toEqual([]);
|
|
180
|
-
});
|
|
181
|
-
vitest_1.it.each([
|
|
182
|
-
['http', 'http://hooks.example.com/n'],
|
|
183
|
-
['localhost', 'https://localhost:3000/n'],
|
|
184
|
-
['a loopback ip', 'https://127.0.0.1/n'],
|
|
185
|
-
['not a URL at all', '/notifications'],
|
|
186
|
-
])('rejects a dispatchUrl on %s', (_label, dispatchUrl) => {
|
|
187
|
-
// Unlike `devUrl`, this URL is called BY Tanqory. A laptop address here
|
|
188
|
-
// is one nothing can ever reach, so it is refused rather than tolerated.
|
|
189
|
-
(0, vitest_1.expect)(problemsOf({ ...VALID, notifications: { dispatchUrl } })).not.toEqual([]);
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
//# sourceMappingURL=manifest.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.spec.js","sourceRoot":"","sources":["../src/manifest.spec.ts"],"names":[],"mappings":"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"}
|
package/dist/webhooks.spec.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"webhooks.spec.d.ts","sourceRoot":"","sources":["../src/webhooks.spec.ts"],"names":[],"mappings":""}
|
package/dist/webhooks.spec.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const node_crypto_1 = require("node:crypto");
|
|
4
|
-
const vitest_1 = require("vitest");
|
|
5
|
-
const webhooks_1 = require("./webhooks");
|
|
6
|
-
/**
|
|
7
|
-
* This function is the only thing standing between an app's webhook handler and
|
|
8
|
-
* anyone on the internet who knows its URL. Every case below is a way that
|
|
9
|
-
* boundary gets crossed by an implementation that "looks right".
|
|
10
|
-
*/
|
|
11
|
-
const SECRET = 'whsec_test';
|
|
12
|
-
const BODY = '{"event":"orders/create","data":{"id":"ord_1"}}';
|
|
13
|
-
function sign(body, timestamp, secret = SECRET) {
|
|
14
|
-
const signature = (0, node_crypto_1.createHmac)('sha256', secret).update(`${timestamp}.${body}`).digest('hex');
|
|
15
|
-
return `t=${timestamp},v1=${signature}`;
|
|
16
|
-
}
|
|
17
|
-
const NOW = 1_700_000_000_000;
|
|
18
|
-
const now = () => NOW;
|
|
19
|
-
const nowSeconds = Math.floor(NOW / 1000);
|
|
20
|
-
(0, vitest_1.describe)('verifyWebhookSignature', () => {
|
|
21
|
-
(0, vitest_1.it)('accepts a correctly signed, fresh payload', () => {
|
|
22
|
-
(0, vitest_1.expect)((0, webhooks_1.verifyWebhookSignature)(BODY, sign(BODY, nowSeconds), SECRET, { now })).toBe(true);
|
|
23
|
-
});
|
|
24
|
-
(0, vitest_1.it)('rejects a body that changed after signing', () => {
|
|
25
|
-
const header = sign(BODY, nowSeconds);
|
|
26
|
-
const tampered = BODY.replace('ord_1', 'ord_999');
|
|
27
|
-
(0, vitest_1.expect)((0, webhooks_1.verifyWebhookSignature)(tampered, header, SECRET, { now })).toBe(false);
|
|
28
|
-
});
|
|
29
|
-
(0, vitest_1.it)('rejects a signature made with a different secret', () => {
|
|
30
|
-
const header = sign(BODY, nowSeconds, 'whsec_someone_elses');
|
|
31
|
-
(0, vitest_1.expect)((0, webhooks_1.verifyWebhookSignature)(BODY, header, SECRET, { now })).toBe(false);
|
|
32
|
-
});
|
|
33
|
-
(0, vitest_1.describe)('replay', () => {
|
|
34
|
-
(0, vitest_1.it)('rejects a payload older than the tolerance', () => {
|
|
35
|
-
// Without this, a captured delivery verifies forever.
|
|
36
|
-
const header = sign(BODY, nowSeconds - 301);
|
|
37
|
-
(0, vitest_1.expect)((0, webhooks_1.verifyWebhookSignature)(BODY, header, SECRET, { now })).toBe(false);
|
|
38
|
-
});
|
|
39
|
-
(0, vitest_1.it)('accepts one just inside the tolerance', () => {
|
|
40
|
-
const header = sign(BODY, nowSeconds - 299);
|
|
41
|
-
(0, vitest_1.expect)((0, webhooks_1.verifyWebhookSignature)(BODY, header, SECRET, { now })).toBe(true);
|
|
42
|
-
});
|
|
43
|
-
(0, vitest_1.it)('rejects a timestamp in the future', () => {
|
|
44
|
-
// Not a clock problem to be generous about — a future timestamp means
|
|
45
|
-
// someone chose it.
|
|
46
|
-
const header = sign(BODY, nowSeconds + 301);
|
|
47
|
-
(0, vitest_1.expect)((0, webhooks_1.verifyWebhookSignature)(BODY, header, SECRET, { now })).toBe(false);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
(0, vitest_1.describe)('malformed input is rejected, never thrown on', () => {
|
|
51
|
-
// A handler that 500s on a malformed header tells an attacker their probe
|
|
52
|
-
// reached real code. Every one of these must simply be false.
|
|
53
|
-
const bad = [
|
|
54
|
-
['missing header', null],
|
|
55
|
-
['empty header', ''],
|
|
56
|
-
['no timestamp', 'v1=abc'],
|
|
57
|
-
['no signature', `t=${nowSeconds}`],
|
|
58
|
-
['non-numeric timestamp', 't=not-a-number,v1=abc'],
|
|
59
|
-
['garbage', 'nonsense'],
|
|
60
|
-
];
|
|
61
|
-
vitest_1.it.each(bad)('rejects %s', (_label, header) => {
|
|
62
|
-
(0, vitest_1.expect)((0, webhooks_1.verifyWebhookSignature)(BODY, header, SECRET, { now })).toBe(false);
|
|
63
|
-
});
|
|
64
|
-
(0, vitest_1.it)('rejects when no secret is configured', () => {
|
|
65
|
-
(0, vitest_1.expect)((0, webhooks_1.verifyWebhookSignature)(BODY, sign(BODY, nowSeconds), '', { now })).toBe(false);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
(0, vitest_1.it)('rejects a signature of the right shape but the wrong length', () => {
|
|
69
|
-
// Guards the constant-time compare: a length mismatch must return false,
|
|
70
|
-
// not throw, or the throw itself becomes the timing signal.
|
|
71
|
-
(0, vitest_1.expect)((0, webhooks_1.verifyWebhookSignature)(BODY, `t=${nowSeconds},v1=abc`, SECRET, { now })).toBe(false);
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
//# sourceMappingURL=webhooks.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"webhooks.spec.js","sourceRoot":"","sources":["../src/webhooks.spec.ts"],"names":[],"mappings":"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"}
|