cursedbelt-core 2.13.1 → 2.13.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/dist/core/analytics/range.d.ts +2 -2
- package/dist/core/analytics/range.js +2 -2
- package/dist/core/analytics/schedule.d.ts +1 -1
- package/dist/core/analytics/schedule.js +3 -3
- package/dist/core/duplicates/index.d.ts +2 -2
- package/dist/core/duplicates/index.js +1 -1
- package/dist/core/file-tree/fileTreeModel.d.ts +3 -3
- package/dist/core/file-tree/fileTreeModel.js +2 -2
- package/dist/core/folder-tree/actions.d.ts +1 -1
- package/dist/core/folder-tree/actions.js +1 -1
- package/dist/core/master-lock/kdf.js +1 -1
- package/dist/core/master-lock/presence.d.ts +1 -1
- package/dist/core/master-lock/presence.js +1 -1
- package/dist/core/master-lock/wire.d.ts +1 -1
- package/dist/core/media/mediaItem.js +1 -1
- package/dist/core/media/types.d.ts +4 -4
- package/dist/core/media/videoCuts.d.ts +1 -1
- package/dist/core/media/videoCuts.js +2 -2
- package/dist/core/navigation/index.js +1 -1
- package/dist/core/navigation/navigationModel.d.ts +1 -1
- package/dist/core/navigation/navigationModel.js +1 -1
- package/dist/core/navigation/navigationSelectors.d.ts +2 -2
- package/dist/core/navigation/navigationSelectors.js +2 -2
- package/dist/core/navigation/searchCatalogue.d.ts +2 -2
- package/dist/core/navigation/searchCatalogue.js +4 -4
- package/dist/core/palette/fuzzyRank.d.ts +1 -1
- package/dist/core/palette/fuzzyRank.js +3 -3
- package/dist/core/tokens/registry.d.ts +1 -1
- package/dist/core/tokens/registry.js +1 -1
- package/dist/core/wire/__fixtures__/rowsMarshalWorker.js +2 -2
- package/dist/core/wire/index.d.ts +1 -1
- package/dist/core/wire/index.js +1 -1
- package/dist/core/wire/ingress.d.ts +4 -4
- package/dist/core/wire/ingress.js +12 -12
- package/dist/core/wire/marshalWorker.d.ts +1 -1
- package/dist/core/wire/marshalWorker.js +15 -15
- package/dist/core/wire/marshalling.d.ts +16 -16
- package/dist/core/wire/marshalling.js +13 -13
- package/dist/ctgrClient/convertRequest.js +21 -5
- package/dist/testing/bytes.d.ts +1 -1
- package/dist/testing/bytes.js +1 -1
- package/package.json +1 -1
- package/src/core/analytics/range.spec.ts +1 -1
- package/src/core/analytics/range.ts +2 -2
- package/src/core/analytics/schedule.spec.ts +3 -3
- package/src/core/analytics/schedule.ts +3 -3
- package/src/core/duplicates/index.ts +2 -2
- package/src/core/file-tree/fileTreeModel.ts +4 -4
- package/src/core/folder-tree/actions.ts +1 -1
- package/src/core/master-lock/kdf.ts +1 -1
- package/src/core/master-lock/presence.ts +1 -1
- package/src/core/master-lock/wire.ts +1 -1
- package/src/core/media/mediaItem.ts +1 -1
- package/src/core/media/types.ts +4 -4
- package/src/core/media/videoCuts.ts +2 -2
- package/src/core/navigation/index.ts +1 -1
- package/src/core/navigation/navigation.spec.ts +3 -3
- package/src/core/navigation/navigationModel.ts +2 -2
- package/src/core/navigation/navigationSelectors.ts +3 -3
- package/src/core/navigation/searchCatalogue.ts +4 -4
- package/src/core/palette/fuzzyRank.ts +3 -3
- package/src/core/tokens/registry.ts +2 -2
- package/src/core/wire/__fixtures__/rowsMarshalWorker.ts +2 -2
- package/src/core/wire/index.ts +1 -1
- package/src/core/wire/ingress.spec.ts +12 -12
- package/src/core/wire/ingress.ts +15 -15
- package/src/core/wire/marshalWorker.spec.ts +7 -7
- package/src/core/wire/marshalWorker.ts +15 -15
- package/src/core/wire/marshalling.spec.ts +3 -3
- package/src/core/wire/marshalling.ts +25 -25
- package/src/ctgrClient/convertRequest.ts +21 -4
- package/src/ctgrClient/ctgrClient.spec.ts +32 -0
- package/src/namedSubpathsResolve.spec.ts +1 -1
- package/src/publishShape.spec.ts +1 -1
- package/src/shippedFilesAreTracked.spec.ts +2 -2
- package/src/testing/bytes.ts +1 -1
|
@@ -57,7 +57,7 @@ export async function convertIfOnlyGETRequests(
|
|
|
57
57
|
throw new Error(`ctgr: "${method}" is not a tunnelable method`);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
const body: unknown = form ? await formDataToBody(form) : bodyFromInit(init.body);
|
|
60
|
+
const body: unknown = form ? await formDataToBody(form) : bodyFromInit(init.body, upper, url);
|
|
61
61
|
const getInit = toGetInit(init);
|
|
62
62
|
const { chunks } = await encodeRequest(upper, url, body);
|
|
63
63
|
return { requests: chunks.map((chunk) => ({ url: chunk.url, init: getInit })) };
|
|
@@ -89,9 +89,26 @@ async function formDataToBody(formData: FormData): Promise<Record<string, unknow
|
|
|
89
89
|
return { __files: files, ...fields };
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
/**
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
/**
|
|
93
|
+
* The request body as a JS value: a JSON string is parsed, a plain string rides as itself.
|
|
94
|
+
*
|
|
95
|
+
* 🔴 Anything else is REFUSED. The tunnel carries a JSON value or a form's files; a `File`, a
|
|
96
|
+
* `Blob`, bytes, a stream or `URLSearchParams` handed through here was JSON-serialized as `{}` —
|
|
97
|
+
* desk's import preview posted a 3.4 MB workbook and the server described "a JSON file" with `{}`
|
|
98
|
+
* as its sample (forge task 3443). A loud error at the call is the bug; a silent `{}` is data loss.
|
|
99
|
+
* Send an upload as `FormData`, which is carried whole.
|
|
100
|
+
*/
|
|
101
|
+
function bodyFromInit(body: BodyInit | null | undefined, method: string, url: string): unknown {
|
|
102
|
+
if (body === null || body === undefined) return undefined;
|
|
103
|
+
if (typeof body !== 'string') {
|
|
104
|
+
// A plain object or array a caller cast to `BodyInit` serializes as itself — carried as before.
|
|
105
|
+
const proto = Object.getPrototypeOf(body);
|
|
106
|
+
if (Array.isArray(body) || proto === Object.prototype || proto === null) return body;
|
|
107
|
+
const shape = Object.prototype.toString.call(body).slice(8, -1);
|
|
108
|
+
throw new Error(
|
|
109
|
+
`ctgr: a ${shape} body cannot ride the GET-only tunnel (${method} ${url}) — send it as FormData or a JSON string`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
95
112
|
try {
|
|
96
113
|
return JSON.parse(body);
|
|
97
114
|
} catch {
|
|
@@ -132,4 +132,36 @@ describe('convertIfOnlyGETRequests', () => {
|
|
|
132
132
|
expect(decoded.files?.[0]).toMatchObject({ originalname: 'p.bin', mimetype: 'image/png' });
|
|
133
133
|
expect(decoded.body).toEqual({ name: 'pic' });
|
|
134
134
|
});
|
|
135
|
+
|
|
136
|
+
// forge task 3443: desk posted a workbook as a raw `File`, and it arrived as `{}`.
|
|
137
|
+
test('mode ON: a body the tunnel cannot carry is refused, never sent as {}', async () => {
|
|
138
|
+
setCtgrMode(true);
|
|
139
|
+
const bodies: BodyInit[] = [
|
|
140
|
+
new File([new Uint8Array([0x50, 0x4b, 3, 4])], 'budget.xlsx'),
|
|
141
|
+
new Blob(['x']),
|
|
142
|
+
new Uint8Array([1, 2, 3]),
|
|
143
|
+
new Uint8Array([1, 2, 3]).buffer,
|
|
144
|
+
new URLSearchParams({ a: '1' }),
|
|
145
|
+
];
|
|
146
|
+
for (const body of bodies) {
|
|
147
|
+
await expect(convertIfOnlyGETRequests('/api/v1/files/preview', { method: 'POST', body })).rejects.toThrow(
|
|
148
|
+
/cannot ride the GET-only tunnel \(POST \/api\/v1\/files\/preview\) — send it as FormData/,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test('mode ON: a plain object a caller cast to BodyInit still rides as JSON', async () => {
|
|
154
|
+
setCtgrMode(true);
|
|
155
|
+
const body = { name: 'Budget' } as unknown as BodyInit;
|
|
156
|
+
const { requests } = await convertIfOnlyGETRequests('/api/v1/files', { method: 'POST', body });
|
|
157
|
+
const decoded = await decodeRequest(new URL(requests[0].url, 'http://x').searchParams);
|
|
158
|
+
if (decoded === 'partial') throw new Error('unreachable');
|
|
159
|
+
expect(decoded.body).toEqual({ name: 'Budget' });
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('mode OFF: any body passes through untouched — only the tunnel is particular', async () => {
|
|
163
|
+
const body = new Blob(['x']);
|
|
164
|
+
const { requests } = await convertIfOnlyGETRequests('/api/v1/files/preview', { method: 'POST', body });
|
|
165
|
+
expect(requests[0].init.body).toBe(body);
|
|
166
|
+
});
|
|
135
167
|
});
|
|
@@ -103,7 +103,7 @@ describe('subpaths named in this repo resolve', () => {
|
|
|
103
103
|
});
|
|
104
104
|
|
|
105
105
|
it('knows what the manifest exports', () => {
|
|
106
|
-
// If `exports` is ever read wrong, every
|
|
106
|
+
// If `exports` is ever read wrong, every offense below silently becomes one.
|
|
107
107
|
expect(exported.has('')).toBe(true); // the "." root entry
|
|
108
108
|
expect(exported.has('/master-lock')).toBe(true);
|
|
109
109
|
expect(exported.has('/react')).toBe(false); // the react tier stayed in `cursedbelt` at the 2026-09-15 split
|
package/src/publishShape.spec.ts
CHANGED
|
@@ -65,7 +65,7 @@ describe('publish shape', () => {
|
|
|
65
65
|
// npm rejects a "./"-prefixed bin value, calls the NAME invalid, and drops
|
|
66
66
|
// the entry — with a warning, not an error. The package publishes fine and
|
|
67
67
|
// installs with no CLI at all. Invisible under a file: link, where the bin
|
|
68
|
-
// is linked from the source manifest before npm ever
|
|
68
|
+
// is linked from the source manifest before npm ever normalizes it.
|
|
69
69
|
const bin = (pkg as { bin?: Record<string, string> }).bin ?? {};
|
|
70
70
|
const prefixed = Object.entries(bin).filter(([, v]) => v.startsWith('./'));
|
|
71
71
|
expect(
|
|
@@ -53,8 +53,8 @@ describe('every file inside the published surface is tracked', () => {
|
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
test('no untracked file sits inside a directory `files` ships', async () => {
|
|
56
|
-
// --others = untracked; --exclude-standard
|
|
57
|
-
// ignored paths (.agent.noindex, node_modules) are not
|
|
56
|
+
// --others = untracked; --exclude-standard honors .gitignore, so deliberately
|
|
57
|
+
// ignored paths (.agent.noindex, node_modules) are not offenses.
|
|
58
58
|
const { ok, out } = await git('ls-files', '--others', '--exclude-standard', '--', ...shippedDirs());
|
|
59
59
|
expect(ok, 'git ls-files failed').toBe(true);
|
|
60
60
|
const untracked = out === '' ? [] : out.split('\n');
|
package/src/testing/bytes.ts
CHANGED
|
@@ -32,7 +32,7 @@ export const asBody = (bytes: Uint8Array): Uint8Array<ArrayBuffer> => bytes as U
|
|
|
32
32
|
export const bytesResponse = (bytes: Uint8Array, init?: ResponseInit): Response =>
|
|
33
33
|
new Response(asBody(bytes), init);
|
|
34
34
|
|
|
35
|
-
/** A readable stream of `bytes` — what the media
|
|
35
|
+
/** A readable stream of `bytes` — what the media catalog's ingest paths take. */
|
|
36
36
|
export const bytesStream = (bytes: Uint8Array | string): ReadableStream<Uint8Array> =>
|
|
37
37
|
new Response(typeof bytes === 'string' ? asBody(new TextEncoder().encode(bytes)) : asBody(bytes))
|
|
38
38
|
.body as ReadableStream<Uint8Array>;
|