@sqlrooms/utils 0.0.0
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/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-dev.log +203 -0
- package/.turbo/turbo-lint.log +16 -0
- package/.turbo/turbo-test.log +51 -0
- package/CHANGELOG.md +8 -0
- package/LICENSE.md +9 -0
- package/dist/color.d.ts +2 -0
- package/dist/color.d.ts.map +1 -0
- package/dist/color.js +10 -0
- package/dist/format.d.ts +11 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +10 -0
- package/dist/helpers.d.ts +11 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +56 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/storage.d.ts +10 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +9 -0
- package/dist/str.d.ts +13 -0
- package/dist/str.d.ts.map +1 -0
- package/dist/str.js +122 -0
- package/dist/str.test.d.ts +2 -0
- package/dist/str.test.d.ts.map +1 -0
- package/dist/str.test.js +42 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/xhr.d.ts +20 -0
- package/dist/xhr.d.ts.map +1 -0
- package/dist/xhr.js +94 -0
- package/eslint.config.js +4 -0
- package/jest.config.ms +8 -0
- package/package.json +36 -0
- package/src/color.ts +11 -0
- package/src/format.ts +12 -0
- package/src/helpers.ts +68 -0
- package/src/index.ts +6 -0
- package/src/storage.ts +14 -0
- package/src/str.test.ts +65 -0
- package/src/str.ts +166 -0
- package/src/xhr.ts +124 -0
- package/tsconfig.json +5 -0
package/dist/str.test.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { convertToUniqueS3ObjectName, generateUniqueName, generateUniquePath, } from './str';
|
|
2
|
+
test('generateUniqueName generates unique table names', () => {
|
|
3
|
+
expect(generateUniqueName('foo', [])).toBe('foo');
|
|
4
|
+
expect(generateUniqueName('foo', ['345'])).toBe('foo');
|
|
5
|
+
expect(generateUniqueName('foo', ['foo_1'])).toBe('foo');
|
|
6
|
+
expect(generateUniqueName('foo', ['foo', 'foo_1'])).toBe('foo_2');
|
|
7
|
+
expect(generateUniqueName('foo_1', ['foo_1'])).toBe('foo_2');
|
|
8
|
+
expect(generateUniqueName('foo_2', ['foo_2'])).toBe('foo_3');
|
|
9
|
+
expect(generateUniqueName('foo', ['foo', 'foo_1', 'foo_2'])).toBe('foo_3');
|
|
10
|
+
expect(generateUniqueName('')).toBe('');
|
|
11
|
+
expect(generateUniqueName('', [''])).toBe('_1');
|
|
12
|
+
expect(generateUniqueName('_', ['_'])).toBe('__1');
|
|
13
|
+
});
|
|
14
|
+
test('generateUniquePath generates unique paths', () => {
|
|
15
|
+
expect(generateUniquePath('/foo/bar.csv', [])).toBe('/foo/bar.csv');
|
|
16
|
+
expect(generateUniquePath('/foo/bar.csv', ['345'])).toBe('/foo/bar.csv');
|
|
17
|
+
expect(generateUniquePath('/foo/bar.csv', ['/foo/bar.csv'])).toBe('/foo/bar_1.csv');
|
|
18
|
+
expect(generateUniquePath('/foo/bar.csv', ['/foo/bar_1.csv'])).toBe('/foo/bar.csv');
|
|
19
|
+
expect(generateUniquePath('/foo/bar.csv', ['/foo/bar.csv', '/foo/bar_1.csv'])).toBe('/foo/bar_2.csv');
|
|
20
|
+
expect(generateUniquePath('/foo/bar.csv', [
|
|
21
|
+
'/foo/bar.csv',
|
|
22
|
+
'/foo/bar_1.csv',
|
|
23
|
+
'/foo/bar_2.csv',
|
|
24
|
+
])).toBe('/foo/bar_3.csv');
|
|
25
|
+
expect(generateUniquePath('/foo/bar_1.csv', ['/foo/bar_1.csv'])).toBe('/foo/bar_2.csv');
|
|
26
|
+
expect(generateUniquePath('/foo/bar_2.csv', ['/foo/bar_2.csv'])).toBe('/foo/bar_3.csv');
|
|
27
|
+
expect(generateUniquePath('/foo', [])).toBe('/foo');
|
|
28
|
+
expect(generateUniquePath('/foo/bar', [])).toBe('/foo/bar');
|
|
29
|
+
expect(generateUniquePath('/foo/bar', ['/foo/bar'])).toBe('/foo/bar_1');
|
|
30
|
+
expect(generateUniquePath('/', [])).toBe('/');
|
|
31
|
+
expect(generateUniquePath('', [])).toBe('');
|
|
32
|
+
expect(generateUniquePath('', [''])).toBe('_1');
|
|
33
|
+
expect(generateUniquePath('_', ['_'])).toBe('__1');
|
|
34
|
+
});
|
|
35
|
+
test('convertToUniqueS3ObjectName works correctly', () => {
|
|
36
|
+
expect(convertToUniqueS3ObjectName('/flows.csv', [])).toBe('_flows.csv');
|
|
37
|
+
expect(convertToUniqueS3ObjectName('&flows.csv', [])).toBe('_flows.csv');
|
|
38
|
+
expect(convertToUniqueS3ObjectName('@flows.csv', [])).toBe('_flows.csv');
|
|
39
|
+
expect(convertToUniqueS3ObjectName('/flows.csv', [])).toBe('_flows.csv');
|
|
40
|
+
expect(convertToUniqueS3ObjectName('flows.csv', ['flows.csv'])).toBe('flows_1.csv');
|
|
41
|
+
expect(convertToUniqueS3ObjectName('flows.csv', ['flows.csv', 'flows_1.csv'])).toBe('flows_2.csv');
|
|
42
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"program":{"fileNames":["../../../node_modules/.pnpm/typescript@5.5.4/node_modules/typescript/lib/lib.es5.d.ts","../../../node_modules/.pnpm/typescript@5.5.4/node_modules/typescript/lib/lib.es2015.d.ts","../../../node_modules/.pnpm/typescript@5.5.4/node_modules/typescript/lib/lib.es2016.d.ts","../../../node_modules/.pnpm/typescript@5.5.4/node_modules/typescript/lib/lib.dom.d.ts","../../../node_modules/.pnpm/typescript@5.5.4/node_modules/typescript/lib/lib.dom.iterable.d.ts","../../../node_modules/.pnpm/typescript@5.5.4/node_modules/typescript/lib/lib.es2015.core.d.ts","../../../node_modules/.pnpm/typescript@5.5.4/node_modules/typescript/lib/lib.es2015.collection.d.ts","../../../node_modules/.pnpm/typescript@5.5.4/node_modules/typescript/lib/lib.es2015.generator.d.ts","../../../node_modules/.pnpm/typescript@5.5.4/node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../../node_modules/.pnpm/typescript@5.5.4/node_modules/typescript/lib/lib.es2015.promise.d.ts","../../../node_modules/.pnpm/typescript@5.5.4/node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../../node_modules/.pnpm/typescript@5.5.4/node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../../node_modules/.pnpm/typescript@5.5.4/node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../../node_modules/.pnpm/typescript@5.5.4/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../../node_modules/.pnpm/typescript@5.5.4/node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../../node_modules/.pnpm/typescript@5.5.4/node_modules/typescript/lib/lib.es2016.intl.d.ts","../../../node_modules/.pnpm/typescript@5.5.4/node_modules/typescript/lib/lib.decorators.d.ts","../../../node_modules/.pnpm/typescript@5.5.4/node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../../node_modules/.pnpm/@types+react@18.3.18/node_modules/@types/react/global.d.ts","../../../node_modules/.pnpm/csstype@3.1.3/node_modules/csstype/index.d.ts","../../../node_modules/.pnpm/@types+prop-types@15.7.14/node_modules/@types/prop-types/index.d.ts","../../../node_modules/.pnpm/@types+react@18.3.18/node_modules/@types/react/index.d.ts","../../../node_modules/.pnpm/@types+react@18.3.18/node_modules/@types/react/jsx-runtime.d.ts","../../../node_modules/.pnpm/@types+d3-color@3.1.3/node_modules/@types/d3-color/index.d.ts","../src/color.ts","../../../node_modules/.pnpm/@types+d3-format@3.0.4/node_modules/@types/d3-format/index.d.ts","../src/format.ts","../../../node_modules/.pnpm/@types+d3-time-format@4.0.3/node_modules/@types/d3-time-format/index.d.ts","../../../node_modules/.pnpm/@types+seedrandom@3.0.8/node_modules/@types/seedrandom/index.d.ts","../../../node_modules/.pnpm/dayjs@1.11.13/node_modules/dayjs/locale/types.d.ts","../../../node_modules/.pnpm/dayjs@1.11.13/node_modules/dayjs/locale/index.d.ts","../../../node_modules/.pnpm/dayjs@1.11.13/node_modules/dayjs/index.d.ts","../../../node_modules/.pnpm/dayjs@1.11.13/node_modules/dayjs/plugin/relativetime.d.ts","../src/helpers.ts","../src/xhr.ts","../src/storage.ts","../src/str.ts","../src/index.ts","../src/str.test.ts","../../../node_modules/.pnpm/@jest+expect-utils@29.7.0/node_modules/@jest/expect-utils/build/index.d.ts","../../../node_modules/.pnpm/chalk@4.1.2/node_modules/chalk/index.d.ts","../../../node_modules/.pnpm/@sinclair+typebox@0.27.8/node_modules/@sinclair/typebox/typebox.d.ts","../../../node_modules/.pnpm/@jest+schemas@29.6.3/node_modules/@jest/schemas/build/index.d.ts","../../../node_modules/.pnpm/pretty-format@29.7.0/node_modules/pretty-format/build/index.d.ts","../../../node_modules/.pnpm/jest-diff@29.7.0/node_modules/jest-diff/build/index.d.ts","../../../node_modules/.pnpm/jest-matcher-utils@29.7.0/node_modules/jest-matcher-utils/build/index.d.ts","../../../node_modules/.pnpm/expect@29.7.0/node_modules/expect/build/index.d.ts","../../../node_modules/.pnpm/@types+jest@29.5.14/node_modules/@types/jest/index.d.ts","../../../node_modules/.pnpm/@types+mocha@10.0.10/node_modules/@types/mocha/index.d.ts","../../../node_modules/.pnpm/@types+react-dom@18.3.5_@types+react@18.3.18/node_modules/@types/react-dom/index.d.ts"],"fileInfos":[{"version":"44e584d4f6444f58791784f1d530875970993129442a847597db702a073ca68c","affectsGlobalScope":true},"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75",{"version":"4af6b0c727b7a2896463d512fafd23634229adf69ac7c00e2ae15a09cb084fad","affectsGlobalScope":true},{"version":"9c00a480825408b6a24c63c1b71362232927247595d7c97659bc24dc68ae0757","affectsGlobalScope":true},{"version":"6920e1448680767498a0b77c6a00a8e77d14d62c3da8967b171f1ddffa3c18e4","affectsGlobalScope":true},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true},{"version":"4443e68b35f3332f753eacc66a04ac1d2053b8b035a0e0ac1d455392b5e243b3","affectsGlobalScope":true},{"version":"bc47685641087c015972a3f072480889f0d6c65515f12bd85222f49a98952ed7","affectsGlobalScope":true},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true},{"version":"93495ff27b8746f55d19fcbcdbaccc99fd95f19d057aed1bd2c0cafe1335fbf0","affectsGlobalScope":true},{"version":"6fc23bb8c3965964be8c597310a2878b53a0306edb71d4b5a4dfe760186bcc01","affectsGlobalScope":true},{"version":"ea011c76963fb15ef1cdd7ce6a6808b46322c527de2077b6cfdf23ae6f5f9ec7","affectsGlobalScope":true},{"version":"33358442698bb565130f52ba79bfd3d4d484ac85fe33f3cb1759c54d18201393","affectsGlobalScope":true},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true},{"version":"36a2e4c9a67439aca5f91bb304611d5ae6e20d420503e96c230cf8fcdc948d94","affectsGlobalScope":true},"8a8eb4ebffd85e589a1cc7c178e291626c359543403d58c9cd22b81fab5b1fb9","65ff5a0aefd7817a03c1ad04fee85c9cdd3ec415cc3c9efec85d8008d4d5e4ee",{"version":"b89c2ddec6bd955e8721d41e24ca667de06882338d88b183c2cdc1f41f4c5a34","affectsGlobalScope":true},"42c169fb8c2d42f4f668c624a9a11e719d5d07dacbebb63cbcf7ef365b0a75b3","6fc1a4f64372593767a9b7b774e9b3b92bf04e8785c3f9ea98973aa9f4bbe490","e33a7456f02ff96e9ee751532f9ff26541070229f240ec0e8776c4410590bee5","189266dd5f90a981910c70d7dfa05e2bca901a4f8a2680d7030c3abbfb5b1e23","fc1450f026c1e9019adfa69b8cf6cb593b2f8e39a9a0a509626c171b155e1df2","dd8936160e41420264a9d5fade0ff95cc92cab56032a84c74a46b4c38e43121e","bb5d24e66d38263b1bda38d0f9b7a72aa2a9de2f7dfd840132a2e372bffd95d8",{"version":"73a0ee6395819b063df4b148211985f2e1442945c1a057204cf4cf6281760dc3","affectsGlobalScope":true},"d05d8c67116dceafc62e691c47ac89f8f10cf7313cd1b2fb4fe801c2bf1bb1a7","3c5bb5207df7095882400323d692957e90ec17323ccff5fd5f29a1ecf3b165d0","f684f2969931de8fb9a5164f8c8f51aaea4025f4eede98406a17642a605c2842","1f4390e16ab10668dc316a909d1b1bc42479e06ee4fc129b3c6e6948ac104d92","c13a38d11c837670628513f9de2d2c25272be5574ec1fd61fbe60ad02699aa05","8faa2cfac01b22e2328f7474d30b110ee2ec16ef2cabd1ae3f838e0bb8dc80fa","f1f981926e310e6c6936769f0f8b9b7e7994fd703c75d38589b25d0f47d49a49","fc51df5c5b5ae7f838f977732152b66984adeacdc14f4e55e144a4a5230af4e9","724777dc2b82b308dc37481e4b77b09ec8fa696227547712e01aaacd3d20b7a0","cdcc132f207d097d7d3aa75615ab9a2e71d6a478162dde8b67f88ea19f3e54de","0d14fa22c41fdc7277e6f71473b20ebc07f40f00e38875142335d5b63cdfc9d2","c085e9aa62d1ae1375794c1fb927a445fa105fed891a7e24edbb1c3300f7384a","f315e1e65a1f80992f0509e84e4ae2df15ecd9ef73df975f7c98813b71e4c8da","5b9586e9b0b6322e5bfbd2c29bd3b8e21ab9d871f82346cb71020e3d84bae73e","3e70a7e67c2cb16f8cd49097360c0309fe9d1e3210ff9222e9dac1f8df9d4fb6","ab68d2a3e3e8767c3fba8f80de099a1cfc18c0de79e42cb02ae66e22dfe14a66","d96cc6598148bf1a98fb2e8dcf01c63a4b3558bdaec6ef35e087fd0562eb40ec",{"version":"f8db4fea512ab759b2223b90ecbbe7dae919c02f8ce95ec03f7fb1cf757cfbeb","affectsGlobalScope":true},{"version":"29f72ec1289ae3aeda78bf14b38086d3d803262ac13904b400422941a26a3636","affectsGlobalScope":true},"17ed71200119e86ccef2d96b73b02ce8854b76ad6bd21b5021d4269bec527b5f"],"root":[25,27,[34,39]],"options":{"allowJs":true,"downlevelIteration":true,"esModuleInterop":true,"jsx":4,"module":99,"noImplicitAny":true,"outDir":"./","skipLibCheck":true,"strict":true,"target":99},"fileIdsList":[[42],[44,47],[22],[19,20,21],[31],[30],[32],[40,46],[44],[41,45],[43],[23,24],[23,26],[23,28,29,32,33],[23,25,27,34,35,36,37],[23,35],[23,37],[23]],"referencedMap":[[43,1],[48,2],[50,3],[22,4],[23,3],[32,5],[31,6],[33,7],[47,8],[45,9],[46,10],[44,11],[25,12],[27,13],[34,14],[38,15],[36,16],[39,17],[37,18],[35,18]]},"version":"5.5.4"}
|
package/dist/xhr.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const postData: ({ url, data, }: {
|
|
2
|
+
url: string;
|
|
3
|
+
data?: Record<string, any>;
|
|
4
|
+
}) => Promise<any>;
|
|
5
|
+
export type ProgressInfo = {
|
|
6
|
+
loaded: number;
|
|
7
|
+
total: number;
|
|
8
|
+
ratio: number;
|
|
9
|
+
};
|
|
10
|
+
export declare function downloadFile(url: string, opts?: {
|
|
11
|
+
method?: string;
|
|
12
|
+
headers?: Record<string, string>;
|
|
13
|
+
onProgress?: (info: ProgressInfo) => void;
|
|
14
|
+
}): Promise<Uint8Array>;
|
|
15
|
+
export declare function uploadFile(url: string, content: File | Blob | FormData, opts?: {
|
|
16
|
+
method?: string;
|
|
17
|
+
headers?: Record<string, string>;
|
|
18
|
+
onProgress?: (info: ProgressInfo) => void;
|
|
19
|
+
}): Promise<Response>;
|
|
20
|
+
//# sourceMappingURL=xhr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xhr.d.ts","sourceRoot":"","sources":["../src/xhr.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ,mBAGlB;IACD,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC5B,iBAoBA,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAC,CAAC;AAK1E,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,IAAI,GAAE;IACJ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;CACtC,GACL,OAAO,CAAC,UAAU,CAAC,CAsCrB;AAID,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,IAAI,GAAG,IAAI,GAAG,QAAQ,EAC/B,IAAI,GAAE;IACJ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;CACtC,GACL,OAAO,CAAC,QAAQ,CAAC,CAiCnB"}
|
package/dist/xhr.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
export const postData = async ({ url, data, }) => {
|
|
2
|
+
const res = await fetch(url, {
|
|
3
|
+
method: 'POST',
|
|
4
|
+
headers: new Headers({ 'Content-Type': 'application/json' }),
|
|
5
|
+
credentials: 'same-origin',
|
|
6
|
+
body: JSON.stringify(data),
|
|
7
|
+
});
|
|
8
|
+
if (!res.ok) {
|
|
9
|
+
console.error('Error in postData', { url, data, res });
|
|
10
|
+
let message = res.statusText;
|
|
11
|
+
try {
|
|
12
|
+
message = (await res.json()).error.message;
|
|
13
|
+
console.error(message);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
// ignore
|
|
17
|
+
}
|
|
18
|
+
throw new Error(message);
|
|
19
|
+
}
|
|
20
|
+
return res.json();
|
|
21
|
+
};
|
|
22
|
+
// TODO: use range requests to download in chunks
|
|
23
|
+
// https://github.com/duckdb/duckdb-wasm/blob/d9ea9c919b6301e7c6dc8a9b3fd527e86f69a38e/packages/duckdb-wasm/src/bindings/runtime_browser.ts#L307
|
|
24
|
+
export async function downloadFile(url, opts = {}) {
|
|
25
|
+
const { method = 'GET', headers = {}, onProgress } = opts;
|
|
26
|
+
return await new Promise((resolve, reject) => {
|
|
27
|
+
const xhr = new XMLHttpRequest();
|
|
28
|
+
// https://www.html5rocks.com/en/tutorials/file/xhr2/#toc-bin-data
|
|
29
|
+
xhr.open(method, url, true);
|
|
30
|
+
xhr.responseType = 'arraybuffer';
|
|
31
|
+
Object.keys(headers).map((key) => {
|
|
32
|
+
if (headers[key]) {
|
|
33
|
+
xhr.setRequestHeader(key, headers[key]);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
xhr.onload = () => resolve(new Uint8Array(xhr.response));
|
|
37
|
+
xhr.onreadystatechange = () => {
|
|
38
|
+
if (xhr.readyState === XMLHttpRequest.DONE) {
|
|
39
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
40
|
+
// already handled by onload
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
reject({ status: xhr.status, error: `File download failed` });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
xhr.onerror = () => reject({ status: xhr.status, error: `File download failed` });
|
|
48
|
+
if (onProgress) {
|
|
49
|
+
xhr.onprogress = (event) => {
|
|
50
|
+
const { lengthComputable, loaded, total } = event;
|
|
51
|
+
if (lengthComputable) {
|
|
52
|
+
onProgress({ loaded, total, ratio: total ? loaded / total : 0 });
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
xhr.send(null);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// TODO: upload in chunks https://www.html5rocks.com/en/tutorials/file/xhr2/
|
|
60
|
+
export async function uploadFile(url, content, opts = {}) {
|
|
61
|
+
const { method = 'POST', headers = {}, onProgress } = opts;
|
|
62
|
+
return await new Promise((resolve, reject) => {
|
|
63
|
+
const xhr = new XMLHttpRequest();
|
|
64
|
+
xhr.open(method, url, true);
|
|
65
|
+
Object.keys(headers).map((key) => {
|
|
66
|
+
if (headers[key]) {
|
|
67
|
+
xhr.setRequestHeader(key, headers[key]);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
xhr.onreadystatechange = () => {
|
|
71
|
+
if (xhr.readyState === XMLHttpRequest.DONE) {
|
|
72
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
73
|
+
resolve(xhr.response);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
reject({
|
|
77
|
+
status: xhr.status,
|
|
78
|
+
error: xhr.responseText,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
xhr.onerror = () => reject({ status: xhr.status, error: xhr.responseText });
|
|
84
|
+
if (onProgress) {
|
|
85
|
+
xhr.upload.onprogress = (event) => {
|
|
86
|
+
const { lengthComputable, loaded, total } = event;
|
|
87
|
+
if (lengthComputable) {
|
|
88
|
+
onProgress({ loaded, total, ratio: total ? loaded / total : 0 });
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
xhr.send(content);
|
|
93
|
+
});
|
|
94
|
+
}
|
package/eslint.config.js
ADDED
package/jest.config.ms
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sqlrooms/utils",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "src/index.ts",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"private": false,
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"dev": "tsc -w",
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"_test": "jest",
|
|
16
|
+
"_test:watch": "jest --watch",
|
|
17
|
+
"lint": "eslint ."
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"d3-color": "^3.1.0",
|
|
21
|
+
"d3-format": "^3.1.0",
|
|
22
|
+
"d3-time-format": "^4.1.0",
|
|
23
|
+
"seedrandom": "^3.0.5"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"dayjs": "*",
|
|
27
|
+
"zod": "*"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/d3-color": "^3.1.3",
|
|
31
|
+
"@types/d3-format": "^3.0.4",
|
|
32
|
+
"@types/d3-time-format": "^4.0.3",
|
|
33
|
+
"@types/seedrandom": "^3.0.8"
|
|
34
|
+
},
|
|
35
|
+
"gitHead": "4b0c709542475e4f95db0b2a8405ecadcf2ec186"
|
|
36
|
+
}
|
package/src/color.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {color as d3color} from 'd3-color';
|
|
2
|
+
|
|
3
|
+
export function opacifyHex(hexCode: string, opacity: number): string {
|
|
4
|
+
const c = d3color(hexCode);
|
|
5
|
+
if (!c) {
|
|
6
|
+
console.warn('Invalid color: ', hexCode);
|
|
7
|
+
return `rgba(255, 255, 255, ${opacity})`;
|
|
8
|
+
}
|
|
9
|
+
const col = c.rgb();
|
|
10
|
+
return `rgba(${col.r}, ${col.g}, ${col.b}, ${opacity})`;
|
|
11
|
+
}
|
package/src/format.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as d3Format from 'd3-format';
|
|
2
|
+
|
|
3
|
+
export const formatCount = d3Format.format(',.0f');
|
|
4
|
+
export const formatCount4 = d3Format.format('.4~s');
|
|
5
|
+
export const formatCountShort = d3Format.format(',.0s');
|
|
6
|
+
|
|
7
|
+
export function shorten(str: string, maxLength = 10): string {
|
|
8
|
+
if (str.length <= maxLength) {
|
|
9
|
+
return str;
|
|
10
|
+
}
|
|
11
|
+
return `${str.substring(0, maxLength - 3)}…`;
|
|
12
|
+
}
|
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {timeFormat} from 'd3-time-format';
|
|
2
|
+
import {alea} from 'seedrandom';
|
|
3
|
+
|
|
4
|
+
import dayjs, {ConfigType} from 'dayjs';
|
|
5
|
+
import relativeTime from 'dayjs/plugin/relativeTime';
|
|
6
|
+
|
|
7
|
+
dayjs.extend(relativeTime);
|
|
8
|
+
|
|
9
|
+
export const toDateTime = (secs: number) => {
|
|
10
|
+
const t = new Date('1970-01-01T00:30:00Z'); // Unix epoch start.
|
|
11
|
+
t.setSeconds(secs);
|
|
12
|
+
return t;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function genRandomStr(length: number, seed?: string) {
|
|
16
|
+
const rnd = seed ? alea(seed) : Math.random;
|
|
17
|
+
return Array.from(
|
|
18
|
+
(function* () {
|
|
19
|
+
for (let i = 0; i < length; i++) {
|
|
20
|
+
const v = Math.floor(rnd() * (26 * 2 + 10));
|
|
21
|
+
if (v < 26) {
|
|
22
|
+
yield String.fromCharCode(v + 65); // 'A' - 'Z'
|
|
23
|
+
} else if (v < 52) {
|
|
24
|
+
yield String.fromCharCode(v + 71); // 'a' - 'z'
|
|
25
|
+
} else {
|
|
26
|
+
yield String.fromCharCode(v + 48); // '0' - '9'
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
})(),
|
|
30
|
+
).join('');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const NUMBER_FORMAT = new Intl.NumberFormat('en-US', {
|
|
34
|
+
minimumFractionDigits: 0,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export const formatNumber = (n: number) => NUMBER_FORMAT.format(n);
|
|
38
|
+
|
|
39
|
+
const TIME_OF_DAY_FORMAT = timeFormat('%I:%M %p');
|
|
40
|
+
const DATE_TIME_FORMAT = timeFormat('%a %Y-%m-%d %I:%M %p');
|
|
41
|
+
const DATE_FORMAT = timeFormat('%Y-%m-%d');
|
|
42
|
+
|
|
43
|
+
export const formatDateTime = (d: Date | number | bigint) => {
|
|
44
|
+
const date = d instanceof Date ? d : new Date(Number(d));
|
|
45
|
+
// return date.toISOString();
|
|
46
|
+
return DATE_TIME_FORMAT(date);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const formatDate = (d: Date | number | bigint) => {
|
|
50
|
+
const date = d instanceof Date ? d : new Date(Number(d));
|
|
51
|
+
return DATE_FORMAT(date);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const formatTimeOfDay = (d: Date | number | bigint) => {
|
|
55
|
+
const date = d instanceof Date ? d : new Date(Number(d));
|
|
56
|
+
return TIME_OF_DAY_FORMAT(date);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const formatTimeRelative = (d: ConfigType) => {
|
|
60
|
+
return dayjs().to(d);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const getErrorMessageForDisplay = (e: any) => {
|
|
64
|
+
let msg = e instanceof Error ? e.message : String(e);
|
|
65
|
+
msg = msg.replace(/Query failed: Error: /, '');
|
|
66
|
+
const firstNl = msg.indexOf('\n');
|
|
67
|
+
return firstNl >= 0 ? msg.substring(0, firstNl) : msg;
|
|
68
|
+
};
|
package/src/index.ts
ADDED
package/src/storage.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {postData} from './xhr';
|
|
2
|
+
|
|
3
|
+
// TODO: this should live in app
|
|
4
|
+
export async function getSignedFileUrl(
|
|
5
|
+
params:
|
|
6
|
+
| {fname: string; upload?: boolean}
|
|
7
|
+
| {projectId: string; fname: string; upload?: boolean; password?: string},
|
|
8
|
+
) {
|
|
9
|
+
const {url: locationsUrl} = await postData({
|
|
10
|
+
url: '/api/gen-signed-url',
|
|
11
|
+
data: params,
|
|
12
|
+
});
|
|
13
|
+
return locationsUrl;
|
|
14
|
+
}
|
package/src/str.test.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
convertToUniqueS3ObjectName,
|
|
3
|
+
generateUniqueName,
|
|
4
|
+
generateUniquePath,
|
|
5
|
+
} from './str';
|
|
6
|
+
|
|
7
|
+
test('generateUniqueName generates unique table names', () => {
|
|
8
|
+
expect(generateUniqueName('foo', [])).toBe('foo');
|
|
9
|
+
expect(generateUniqueName('foo', ['345'])).toBe('foo');
|
|
10
|
+
expect(generateUniqueName('foo', ['foo_1'])).toBe('foo');
|
|
11
|
+
expect(generateUniqueName('foo', ['foo', 'foo_1'])).toBe('foo_2');
|
|
12
|
+
expect(generateUniqueName('foo_1', ['foo_1'])).toBe('foo_2');
|
|
13
|
+
expect(generateUniqueName('foo_2', ['foo_2'])).toBe('foo_3');
|
|
14
|
+
expect(generateUniqueName('foo', ['foo', 'foo_1', 'foo_2'])).toBe('foo_3');
|
|
15
|
+
expect(generateUniqueName('')).toBe('');
|
|
16
|
+
expect(generateUniqueName('', [''])).toBe('_1');
|
|
17
|
+
expect(generateUniqueName('_', ['_'])).toBe('__1');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('generateUniquePath generates unique paths', () => {
|
|
21
|
+
expect(generateUniquePath('/foo/bar.csv', [])).toBe('/foo/bar.csv');
|
|
22
|
+
expect(generateUniquePath('/foo/bar.csv', ['345'])).toBe('/foo/bar.csv');
|
|
23
|
+
expect(generateUniquePath('/foo/bar.csv', ['/foo/bar.csv'])).toBe(
|
|
24
|
+
'/foo/bar_1.csv',
|
|
25
|
+
);
|
|
26
|
+
expect(generateUniquePath('/foo/bar.csv', ['/foo/bar_1.csv'])).toBe(
|
|
27
|
+
'/foo/bar.csv',
|
|
28
|
+
);
|
|
29
|
+
expect(
|
|
30
|
+
generateUniquePath('/foo/bar.csv', ['/foo/bar.csv', '/foo/bar_1.csv']),
|
|
31
|
+
).toBe('/foo/bar_2.csv');
|
|
32
|
+
expect(
|
|
33
|
+
generateUniquePath('/foo/bar.csv', [
|
|
34
|
+
'/foo/bar.csv',
|
|
35
|
+
'/foo/bar_1.csv',
|
|
36
|
+
'/foo/bar_2.csv',
|
|
37
|
+
]),
|
|
38
|
+
).toBe('/foo/bar_3.csv');
|
|
39
|
+
expect(generateUniquePath('/foo/bar_1.csv', ['/foo/bar_1.csv'])).toBe(
|
|
40
|
+
'/foo/bar_2.csv',
|
|
41
|
+
);
|
|
42
|
+
expect(generateUniquePath('/foo/bar_2.csv', ['/foo/bar_2.csv'])).toBe(
|
|
43
|
+
'/foo/bar_3.csv',
|
|
44
|
+
);
|
|
45
|
+
expect(generateUniquePath('/foo', [])).toBe('/foo');
|
|
46
|
+
expect(generateUniquePath('/foo/bar', [])).toBe('/foo/bar');
|
|
47
|
+
expect(generateUniquePath('/foo/bar', ['/foo/bar'])).toBe('/foo/bar_1');
|
|
48
|
+
expect(generateUniquePath('/', [])).toBe('/');
|
|
49
|
+
expect(generateUniquePath('', [])).toBe('');
|
|
50
|
+
expect(generateUniquePath('', [''])).toBe('_1');
|
|
51
|
+
expect(generateUniquePath('_', ['_'])).toBe('__1');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('convertToUniqueS3ObjectName works correctly', () => {
|
|
55
|
+
expect(convertToUniqueS3ObjectName('/flows.csv', [])).toBe('_flows.csv');
|
|
56
|
+
expect(convertToUniqueS3ObjectName('&flows.csv', [])).toBe('_flows.csv');
|
|
57
|
+
expect(convertToUniqueS3ObjectName('@flows.csv', [])).toBe('_flows.csv');
|
|
58
|
+
expect(convertToUniqueS3ObjectName('/flows.csv', [])).toBe('_flows.csv');
|
|
59
|
+
expect(convertToUniqueS3ObjectName('flows.csv', ['flows.csv'])).toBe(
|
|
60
|
+
'flows_1.csv',
|
|
61
|
+
);
|
|
62
|
+
expect(
|
|
63
|
+
convertToUniqueS3ObjectName('flows.csv', ['flows.csv', 'flows_1.csv']),
|
|
64
|
+
).toBe('flows_2.csv');
|
|
65
|
+
});
|
package/src/str.ts
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
export function formatBytes(bytes: number): string {
|
|
2
|
+
if (bytes === 0) return '0 Bytes';
|
|
3
|
+
|
|
4
|
+
const k = 1024;
|
|
5
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
|
6
|
+
|
|
7
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
8
|
+
|
|
9
|
+
let sizeValue = bytes / Math.pow(k, i);
|
|
10
|
+
// Use floor to check if there's a non-zero fractional part, format accordingly
|
|
11
|
+
sizeValue =
|
|
12
|
+
sizeValue != Math.floor(sizeValue)
|
|
13
|
+
? parseFloat(sizeValue.toFixed(2))
|
|
14
|
+
: Math.floor(sizeValue);
|
|
15
|
+
|
|
16
|
+
return sizeValue + ' ' + sizes[i];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function splitFilePath(filePath: string): {
|
|
20
|
+
dir: string;
|
|
21
|
+
name: string;
|
|
22
|
+
ext: string;
|
|
23
|
+
} {
|
|
24
|
+
const pathParts = filePath.split('/');
|
|
25
|
+
const file = pathParts.pop() || '';
|
|
26
|
+
|
|
27
|
+
const dotIndex = file.lastIndexOf('.');
|
|
28
|
+
if (dotIndex === -1 || dotIndex === 0)
|
|
29
|
+
return {dir: pathParts.join('/'), name: file, ext: ''};
|
|
30
|
+
|
|
31
|
+
const name = file.substring(0, dotIndex);
|
|
32
|
+
const ext = file.substring(dotIndex + 1);
|
|
33
|
+
|
|
34
|
+
return {dir: pathParts.join('/'), name, ext};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function convertToUniqueColumnOrTableName(
|
|
38
|
+
filename: string,
|
|
39
|
+
existingTables?: string[],
|
|
40
|
+
): string {
|
|
41
|
+
// Remove file extension
|
|
42
|
+
const base = filename.replace(/\.[^/.]+$/, '');
|
|
43
|
+
|
|
44
|
+
// Replace any invalid character with underscore, and convert to lowercase
|
|
45
|
+
let tableName = base.replace(/[^a-z0-9_]/gi, '_');
|
|
46
|
+
|
|
47
|
+
// If the first character is a number, prepend an underscore
|
|
48
|
+
if (/^\d/.test(tableName)) {
|
|
49
|
+
tableName = '_' + tableName;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Truncate to the max length 63
|
|
53
|
+
if (tableName.length > 63) {
|
|
54
|
+
tableName = tableName.substring(0, 63);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
tableName = generateUniqueName(tableName, existingTables);
|
|
58
|
+
|
|
59
|
+
return tableName;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function generateUniqueName(name: string, usedNames?: string[]) {
|
|
63
|
+
const usedNamesLower = usedNames?.map((n) => n.toLowerCase());
|
|
64
|
+
|
|
65
|
+
// If tableName exists in the list
|
|
66
|
+
if (usedNamesLower?.includes(name.toLowerCase())) {
|
|
67
|
+
let baseName: string | undefined = name;
|
|
68
|
+
let i = 0;
|
|
69
|
+
|
|
70
|
+
// If tableName ends with `_${i}` pattern, update the baseTableName and i
|
|
71
|
+
const matched = name.match(/^(.+)_(\d+)$/);
|
|
72
|
+
if (matched) {
|
|
73
|
+
baseName = matched[1];
|
|
74
|
+
i = Number(matched[2]);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
do {
|
|
78
|
+
i++;
|
|
79
|
+
name = `${baseName}_${i}`;
|
|
80
|
+
} while (usedNamesLower.includes(name.toLowerCase()));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return name;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function generateUniquePath(
|
|
87
|
+
filePath: string,
|
|
88
|
+
existingPaths: string[],
|
|
89
|
+
): string {
|
|
90
|
+
let nextPath = filePath;
|
|
91
|
+
if (existingPaths?.includes(filePath)) {
|
|
92
|
+
const {dir, name, ext} = splitFilePath(filePath);
|
|
93
|
+
|
|
94
|
+
let i = 0;
|
|
95
|
+
let baseName: string | undefined = name;
|
|
96
|
+
const matched = name.match(/^(.+)_(\d+)$/);
|
|
97
|
+
if (matched) {
|
|
98
|
+
baseName = matched[1];
|
|
99
|
+
i = Number(matched[2]);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
do {
|
|
103
|
+
i++;
|
|
104
|
+
const fname = `${baseName}_${i}${ext ? `.${ext}` : ''}`;
|
|
105
|
+
nextPath = `${dir}${dir ? '/' : ''}${fname}`;
|
|
106
|
+
} while (existingPaths.includes(nextPath));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return nextPath;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function convertToUniqueS3ObjectName(
|
|
113
|
+
str: string,
|
|
114
|
+
existingObjects?: string[],
|
|
115
|
+
): string {
|
|
116
|
+
let rv = str
|
|
117
|
+
.trim() // Remove leading and trailing white spaces
|
|
118
|
+
.replace(/[^\w\s-\.]/g, '_') // Replace special characters with underscores
|
|
119
|
+
.replace(/\s+/g, '_') // Replace consecutive spaces with a single underscore
|
|
120
|
+
// .replace(/_+/g, '_') // Remove consecutive underscores
|
|
121
|
+
// .replace(/^_/, '') // Remove leading underscores
|
|
122
|
+
// .replace(/_$/, '') // Remove trailing underscores
|
|
123
|
+
.slice(0, 255); // Truncate the string if it exceeds 255 characters
|
|
124
|
+
|
|
125
|
+
if (existingObjects?.length) {
|
|
126
|
+
rv = generateUniquePath(rv, existingObjects);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return rv;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function convertToUniqueS3FolderPath(
|
|
133
|
+
str: string,
|
|
134
|
+
existingObjects?: string[],
|
|
135
|
+
): string {
|
|
136
|
+
let next = convertToUniqueS3ObjectName(str, existingObjects);
|
|
137
|
+
if (!next.endsWith('/')) next += '/'; // Add trailing slash if not present
|
|
138
|
+
return next;
|
|
139
|
+
// return (
|
|
140
|
+
// str
|
|
141
|
+
// .trim() // Remove leading and trailing white spaces
|
|
142
|
+
// .replace(/\/+/g, '/') // Replace consecutive slashes with a single slash
|
|
143
|
+
// .replace(/[^\w\s-\/]/g, '_') // Replace special characters with underscores
|
|
144
|
+
// .replace(/\s+/g, '_') // Replace consecutive spaces with a single underscore
|
|
145
|
+
// .replace(/^\//, '') + // Remove leading slash
|
|
146
|
+
// (str.endsWith('/') ? '' : '/')
|
|
147
|
+
// );
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function camelCaseToTitle(camelCase: string): string {
|
|
151
|
+
// Split the string into words on the camelCase boundaries
|
|
152
|
+
const words = camelCase.match(/^[a-z]+|[A-Z][a-z]*/g);
|
|
153
|
+
|
|
154
|
+
// If words are found, transform them and join into a title string
|
|
155
|
+
if (words) {
|
|
156
|
+
return words
|
|
157
|
+
.map((word) => {
|
|
158
|
+
// Capitalize the first letter of each word
|
|
159
|
+
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
160
|
+
})
|
|
161
|
+
.join(' '); // Join the words with space
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// If no words were found, just capitalize the whole string
|
|
165
|
+
return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
|
|
166
|
+
}
|