@blamejs/blamejs-shop 0.1.13 → 0.1.15
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/CHANGELOG.md +4 -0
- package/lib/admin.js +2 -0
- package/lib/checkout.js +133 -0
- package/lib/payment.js +252 -4
- package/lib/vendor/MANIFEST.json +2 -2
- package/lib/vendor/blamejs/CHANGELOG.md +4 -0
- package/lib/vendor/blamejs/README.md +1 -0
- package/lib/vendor/blamejs/api-snapshot.json +30 -2
- package/lib/vendor/blamejs/lib/structured-fields.js +441 -1
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.12.54.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.12.55.json +18 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/structured-fields-codec.test.js +207 -0
- package/package.json +1 -1
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Layer 0 — b.structuredFields full RFC 8941 codec (parse + serialize).
|
|
4
|
+
* The oracle is a curated set of the official httpwg/structured-field-tests
|
|
5
|
+
* conformance vectors (the same JSON the spec authors maintain): each
|
|
6
|
+
* `raw` parses to the published `expected` value model, each `must_fail`
|
|
7
|
+
* case is rejected, and every passing value round-trips through serialize.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var b = require("../../index");
|
|
11
|
+
var helpers = require("../helpers");
|
|
12
|
+
var check = helpers.check;
|
|
13
|
+
var SF = b.structuredFields;
|
|
14
|
+
|
|
15
|
+
// --- httpwg expected-value normaliser (their format → comparable JSON) ---
|
|
16
|
+
function b32(buf) {
|
|
17
|
+
var A = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", bits = 0, val = 0, out = "";
|
|
18
|
+
for (var i = 0; i < buf.length; i++) { val = (val << 8) | buf[i]; bits += 8; while (bits >= 5) { out += A[(val >> (bits - 5)) & 31]; bits -= 5; } }
|
|
19
|
+
if (bits > 0) out += A[(val << (5 - bits)) & 31];
|
|
20
|
+
while (out.length % 8 !== 0) out += "=";
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
function mineVal(v) {
|
|
24
|
+
if (v instanceof SF.Token) return { token: v.value };
|
|
25
|
+
if (v instanceof SF.ByteSequence) return { binary: b32(v.value) };
|
|
26
|
+
if (v instanceof SF.Decimal) return v.value; // compare a Decimal numerically (httpwg uses plain numbers)
|
|
27
|
+
if (v instanceof SF.Date) return { date: v.value };
|
|
28
|
+
if (v instanceof SF.DisplayString) return { displaystring: v.value };
|
|
29
|
+
return v;
|
|
30
|
+
}
|
|
31
|
+
function mineParams(map) { var o = []; map.forEach(function (v, k) { o.push([k, mineVal(v)]); }); return o; }
|
|
32
|
+
function mineItem(it) { return [mineVal(it.value), mineParams(it.params)]; }
|
|
33
|
+
function mineMember(m) { return Array.isArray(m.items) ? [m.items.map(mineItem), mineParams(m.params)] : mineItem(m); }
|
|
34
|
+
function mine(out, type) {
|
|
35
|
+
if (type === "item") return mineItem(out);
|
|
36
|
+
if (type === "list") return out.map(mineMember);
|
|
37
|
+
var o = []; out.forEach(function (m, k) { o.push([k, mineMember(m)]); }); return o;
|
|
38
|
+
}
|
|
39
|
+
function httpVal(v) {
|
|
40
|
+
if (v && v.__type === "token") return { token: v.value };
|
|
41
|
+
if (v && v.__type === "binary") return { binary: v.value };
|
|
42
|
+
if (v && v.__type === "date") return { date: v.value };
|
|
43
|
+
if (v && v.__type === "displaystring") return { displaystring: v.value };
|
|
44
|
+
return v;
|
|
45
|
+
}
|
|
46
|
+
function httpParams(arr) { return arr.map(function (p) { return [p[0], httpVal(p[1])]; }); }
|
|
47
|
+
function httpItem(it) { return [httpVal(it[0]), httpParams(it[1])]; }
|
|
48
|
+
function httpMember(m) { return Array.isArray(m[0]) ? [m[0].map(httpItem), httpParams(m[1])] : httpItem(m); }
|
|
49
|
+
function http(exp, type) {
|
|
50
|
+
if (type === "item") return httpItem(exp);
|
|
51
|
+
if (type === "list") return exp.map(httpMember);
|
|
52
|
+
return exp.map(function (e) { return [e[0], httpMember(e[1])]; });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Curated cases from httpwg/structured-field-tests (number, string, token,
|
|
56
|
+
// boolean, binary, item, list, dictionary, param-list).
|
|
57
|
+
var CASES = [
|
|
58
|
+
{ name: "basic integer", raw: "42", t: "item", expected: [42, []] },
|
|
59
|
+
{ name: "negative integer", raw: "-42", t: "item", expected: [-42, []] },
|
|
60
|
+
{ name: "negative zero", raw: "-0", t: "item", expected: [0, []] },
|
|
61
|
+
{ name: "basic decimal", raw: "1.5", t: "item", expected: [1.5, []] },
|
|
62
|
+
{ name: "negative decimal", raw: "-1.5", t: "item", expected: [-1.5, []] },
|
|
63
|
+
{ name: "too many int digits", raw: "1111111111111111", t: "item", must_fail: true },
|
|
64
|
+
{ name: "trailing decimal point", raw: "1.", t: "item", must_fail: true },
|
|
65
|
+
{ name: "too many frac digits", raw: "1.1234", t: "item", must_fail: true },
|
|
66
|
+
{ name: "basic string", raw: '"foo bar"', t: "item", expected: ["foo bar", []] },
|
|
67
|
+
{ name: "empty string", raw: '""', t: "item", expected: ["", []] },
|
|
68
|
+
{ name: "escaped quote", raw: '"b\\"a"', t: "item", expected: ['b"a', []] },
|
|
69
|
+
{ name: "unterminated string", raw: '"foo', t: "item", must_fail: true },
|
|
70
|
+
{ name: "basic token", raw: "a_b-c.d3:f%00/*", t: "item", expected: [{ __type: "token", value: "a_b-c.d3:f%00/*" }, []] },
|
|
71
|
+
{ name: "token with capitals", raw: "fooBar", t: "item", expected: [{ __type: "token", value: "fooBar" }, []] },
|
|
72
|
+
{ name: "true boolean", raw: "?1", t: "item", expected: [true, []] },
|
|
73
|
+
{ name: "false boolean", raw: "?0", t: "item", expected: [false, []] },
|
|
74
|
+
{ name: "unknown boolean", raw: "?Q", t: "item", must_fail: true },
|
|
75
|
+
{ name: "basic binary", raw: ":aGVsbG8=:", t: "item", expected: [{ __type: "binary", value: "NBSWY3DP" }, []] },
|
|
76
|
+
{ name: "empty binary", raw: "::", t: "item", expected: [{ __type: "binary", value: "" }, []] },
|
|
77
|
+
{ name: "unpadded binary (RFC 8941 §4.2.7 synthesizes padding)", raw: ":aGVsbG8:", t: "item", expected: [{ __type: "binary", value: "NBSWY3DP" }, []] },
|
|
78
|
+
{ name: "padding at beginning", raw: ":=aGVsbG8=:", t: "item", must_fail: true },
|
|
79
|
+
{ name: "empty item", raw: "", t: "item", must_fail: true },
|
|
80
|
+
{ name: "leading space item", raw: " \t 1", t: "item", must_fail: true },
|
|
81
|
+
{ name: "trailing space item", raw: "1 \t ", t: "item", must_fail: true },
|
|
82
|
+
{ name: "item with param", raw: "5; foo=bar", t: "item", expected: [5, [["foo", { __type: "token", value: "bar" }]]] },
|
|
83
|
+
{ name: "boolean param value", raw: "1; a; b=?0", t: "item", expected: [1, [["a", true], ["b", false]]] },
|
|
84
|
+
{ name: "basic list", raw: "1, 42", t: "list", expected: [[1, []], [42, []]] },
|
|
85
|
+
{ name: "empty list", raw: "", t: "list", expected: [] },
|
|
86
|
+
{ name: "basic inner list", raw: "(1 2)", t: "list", expected: [[[[1, []], [2, []]], []]] },
|
|
87
|
+
{ name: "inner list with params", raw: "(1 2);a=1", t: "list", expected: [[[[1, []], [2, []]], [["a", 1]]]] },
|
|
88
|
+
{ name: "trailing comma list", raw: "1, 42, ", t: "list", must_fail: true },
|
|
89
|
+
{ name: "basic dictionary", raw: "a=1, b=2", t: "dictionary", expected: [["a", [1, []]], ["b", [2, []]]] },
|
|
90
|
+
{ name: "dictionary bare key", raw: "a=1, b, c=3", t: "dictionary", expected: [["a", [1, []]], ["b", [true, []]], ["c", [3, []]]] },
|
|
91
|
+
{ name: "dictionary inner-list value", raw: "a=(1 2)", t: "dictionary", expected: [["a", [[[1, []], [2, []]], []]]] },
|
|
92
|
+
{ name: "trailing comma dict", raw: "a=1,", t: "dictionary", must_fail: true },
|
|
93
|
+
// RFC 9651 Date (§3.3.7)
|
|
94
|
+
{ name: "date epoch", raw: "@0", t: "item", expected: [{ __type: "date", value: 0 }, []] },
|
|
95
|
+
{ name: "date positive", raw: "@1659578233", t: "item", expected: [{ __type: "date", value: 1659578233 }, []] },
|
|
96
|
+
{ name: "date negative", raw: "@-1659578233", t: "item", expected: [{ __type: "date", value: -1659578233 }, []] },
|
|
97
|
+
{ name: "date decimal", raw: "@1659578233.12", t: "item", must_fail: true },
|
|
98
|
+
{ name: "date too large", raw: "@1000000000000000", t: "item", must_fail: true },
|
|
99
|
+
{ name: "date empty", raw: "@", t: "item", must_fail: true },
|
|
100
|
+
{ name: "date sign only", raw: "@-", t: "item", must_fail: true },
|
|
101
|
+
{ name: "date non-digit", raw: "@abc", t: "item", must_fail: true },
|
|
102
|
+
// RFC 9651 Display String (§3.3.8)
|
|
103
|
+
{ name: "ascii display string", raw: '%"foo bar"', t: "item", expected: [{ __type: "displaystring", value: "foo bar" }, []] },
|
|
104
|
+
{ name: "non-ascii display string (lowercase escaping)", raw: '%"f%c3%bc%c3%bc"', t: "item", expected: [{ __type: "displaystring", value: "füü" }, []] },
|
|
105
|
+
{ name: "non-ascii display string (uppercase escaping)", raw: '%"f%C3%BC"', t: "item", must_fail: true },
|
|
106
|
+
{ name: "non-ascii display string (unescaped)", raw: '%"füü"', t: "item", must_fail: true },
|
|
107
|
+
{ name: "display string quoting", raw: '%"foo %22bar%22 \\ baz"', t: "item", expected: [{ __type: "displaystring", value: 'foo "bar" \\ baz' }, []] },
|
|
108
|
+
{ name: "bad display string utf-8", raw: '%"%c3%28"', t: "item", must_fail: true },
|
|
109
|
+
{ name: "bad display string hex", raw: '%"%g0%1w"', t: "item", must_fail: true },
|
|
110
|
+
{ name: "truncated display string escape", raw: '%"%"', t: "item", must_fail: true },
|
|
111
|
+
{ name: "unbalanced display string", raw: '%"foo', t: "item", must_fail: true },
|
|
112
|
+
{ name: "single-quoted display string", raw: "%'foo'", t: "item", must_fail: true },
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
function testConformance() {
|
|
116
|
+
var passed = 0, failed = 0, roundtrips = 0;
|
|
117
|
+
CASES.forEach(function (c) {
|
|
118
|
+
if (c.must_fail) {
|
|
119
|
+
var threw = false;
|
|
120
|
+
try { SF.parse(c.raw, c.t); } catch (_e) { threw = true; }
|
|
121
|
+
if (threw) failed++; else check("must_fail rejected: " + c.name, false);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
var got;
|
|
125
|
+
try { got = SF.parse(c.raw, c.t); }
|
|
126
|
+
catch (_e) { check("parse ok: " + c.name, false); return; }
|
|
127
|
+
var ok = JSON.stringify(mine(got, c.t)) === JSON.stringify(http(c.expected, c.t));
|
|
128
|
+
if (ok) passed++; else check("value matches RFC vector: " + c.name + " (got " + JSON.stringify(mine(got, c.t)) + ")", false);
|
|
129
|
+
// Round-trip: serialize → parse → serialize must be stable.
|
|
130
|
+
try {
|
|
131
|
+
var s1 = SF.serialize(got, c.t);
|
|
132
|
+
var s2 = SF.serialize(SF.parse(s1, c.t), c.t);
|
|
133
|
+
if (s1 === s2) roundtrips++; else check("round-trip stable: " + c.name + " (" + s1 + " vs " + s2 + ")", false);
|
|
134
|
+
} catch (e) { check("round-trip ok: " + c.name + " — " + e.message, false); }
|
|
135
|
+
});
|
|
136
|
+
check("all passing vectors parse to the RFC value model (" + passed + ")", passed === CASES.filter(function (c) { return !c.must_fail; }).length);
|
|
137
|
+
check("all must_fail vectors are rejected (" + failed + ")", failed === CASES.filter(function (c) { return c.must_fail; }).length);
|
|
138
|
+
check("all passing vectors round-trip stably (" + roundtrips + ")", roundtrips === passed);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function testSerialize() {
|
|
142
|
+
check("serialize: token item with param", SF.serialize({ value: new SF.Token("gzip"), params: new Map([["q", 1]]) }, "item") === "gzip;q=1");
|
|
143
|
+
check("serialize: string item", SF.serialize({ value: "a b", params: new Map() }, "item") === '"a b"');
|
|
144
|
+
check("serialize: byte sequence", SF.serialize({ value: new SF.ByteSequence(Buffer.from("hello")), params: new Map() }, "item") === ":aGVsbG8=:");
|
|
145
|
+
check("serialize: list of inner list", SF.serialize([{ items: [{ value: 1, params: new Map() }, { value: 2, params: new Map() }], params: new Map() }], "list") === "(1 2)");
|
|
146
|
+
check("serialize: dictionary from object", SF.serialize({ a: { value: 1, params: new Map() }, b: { value: true, params: new Map() } }, "dictionary") === "a=1, b");
|
|
147
|
+
function code(fn) { try { fn(); return "NO-THROW"; } catch (e) { return e.code; } }
|
|
148
|
+
check("serialize: out-of-range integer refused", code(function () { SF.serialize({ value: 10000000000000000, params: new Map() }, "item"); }) === "structured-fields/serialize");
|
|
149
|
+
check("serialize: invalid token refused", code(function () { SF.serialize({ value: new SF.Token("1bad"), params: new Map() }, "item"); }) === "structured-fields/serialize");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function testDecimalTypePreserved() {
|
|
153
|
+
// A numerically-integral Decimal must NOT serialize back to an Integer.
|
|
154
|
+
var parsed = SF.parse("1.0", "item");
|
|
155
|
+
check("parse: '1.0' yields a Decimal (not a bare integer)", parsed.value instanceof SF.Decimal);
|
|
156
|
+
check("serialize: Decimal 1.0 round-trips to '1.0', not '1'", SF.serialize(parsed, "item") === "1.0");
|
|
157
|
+
check("serialize: explicit SfDecimal forces the decimal form", SF.serialize({ value: new SF.Decimal(5), params: new Map() }, "item") === "5.0");
|
|
158
|
+
check("serialize: a fractional JS number still serializes as a Decimal", SF.serialize({ value: 2.5, params: new Map() }, "item") === "2.5");
|
|
159
|
+
check("serialize: an integral JS number serializes as an Integer", SF.serialize({ value: 5, params: new Map() }, "item") === "5");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function testDisplayStringSurrogate() {
|
|
163
|
+
function code(fn) { try { fn(); return "NO-THROW"; } catch (e) { return e.code; } }
|
|
164
|
+
// A lone UTF-16 surrogate is not a valid Unicode string — serialize must
|
|
165
|
+
// fail rather than silently emit U+FFFD (RFC 9651 §4.1.10).
|
|
166
|
+
check("serialize: lone surrogate display string refused", code(function () { SF.serialize({ value: new SF.DisplayString("a\uD800b"), params: new Map() }, "item"); }) === "structured-fields/serialize");
|
|
167
|
+
// A valid astral code point (surrogate pair) serializes fine.
|
|
168
|
+
check("serialize: astral code point display string ok", SF.serialize({ value: new SF.DisplayString("\u{1F600}"), params: new Map() }, "item") === '%"%f0%9f%98%80"');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function testTypedError() {
|
|
172
|
+
function E(code, msg) { this.code = code; this.message = msg; }
|
|
173
|
+
E.prototype = Object.create(Error.prototype);
|
|
174
|
+
var threw = null;
|
|
175
|
+
try { SF.parse("1.", "item", { ErrorClass: E }); } catch (e) { threw = e; }
|
|
176
|
+
check("parse: typed ErrorClass honored", threw instanceof E && threw.code === "structured-fields/parse");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function testSurface() {
|
|
180
|
+
// Reference the full b.structuredFields.* paths so the coverage gate
|
|
181
|
+
// sees them (the suite otherwise uses the SF alias).
|
|
182
|
+
check("b.structuredFields.parse parses an item", b.structuredFields.parse("42", "item").value === 42);
|
|
183
|
+
check("b.structuredFields.serialize round-trips an item", b.structuredFields.serialize({ value: 42, params: new Map() }, "item") === "42");
|
|
184
|
+
check("b.structuredFields.Token constructs a token", new b.structuredFields.Token("gzip").value === "gzip");
|
|
185
|
+
check("b.structuredFields.ByteSequence constructs a byte sequence", Buffer.isBuffer(new b.structuredFields.ByteSequence(Buffer.from("x")).value));
|
|
186
|
+
check("b.structuredFields.Decimal constructs a decimal", new b.structuredFields.Decimal(1.5).value === 1.5);
|
|
187
|
+
check("b.structuredFields.Date round-trips", b.structuredFields.serialize({ value: new b.structuredFields.Date(1659578233), params: new Map() }, "item") === "@1659578233");
|
|
188
|
+
check("b.structuredFields.DisplayString escapes non-ASCII", b.structuredFields.serialize({ value: new b.structuredFields.DisplayString("füü"), params: new Map() }, "item") === '%"f%c3%bc%c3%bc"');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async function run() {
|
|
192
|
+
testSurface();
|
|
193
|
+
testConformance();
|
|
194
|
+
testSerialize();
|
|
195
|
+
testDecimalTypePreserved();
|
|
196
|
+
testDisplayStringSurrogate();
|
|
197
|
+
testTypedError();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
module.exports = { run: run };
|
|
201
|
+
|
|
202
|
+
if (require.main === module) {
|
|
203
|
+
run().then(
|
|
204
|
+
function () { console.log("[structured-fields-codec] OK — " + helpers.getChecks() + " checks passed"); },
|
|
205
|
+
function (e) { console.error("FAIL:", e && e.stack || e); process.exit(1); }
|
|
206
|
+
);
|
|
207
|
+
}
|
package/package.json
CHANGED