diff --git a/.changeset/cool-seas-talk.md b/.changeset/cool-seas-talk.md new file mode 100644 index 00000000..1730899f --- /dev/null +++ b/.changeset/cool-seas-talk.md @@ -0,0 +1,5 @@ +--- +'@supabase/ssr': patch +--- + +Add cookie chunker methods and expose them to the SDK diff --git a/.changeset/new-dodos-joke.md b/.changeset/new-dodos-joke.md new file mode 100644 index 00000000..d4ce2186 --- /dev/null +++ b/.changeset/new-dodos-joke.md @@ -0,0 +1,5 @@ +--- +'@supabase/auth-helpers-shared': patch +--- + +Fix chunkCount to get the correct number of chunks diff --git a/packages/shared/src/chunker.ts b/packages/shared/src/chunker.ts index 59fe9c74..1032793a 100644 --- a/packages/shared/src/chunker.ts +++ b/packages/shared/src/chunker.ts @@ -17,7 +17,7 @@ export function createChunks(key: string, value: string, chunkSize?: number): Ch const re = chunkSize !== undefined ? createChunkRegExp(chunkSize) : MAX_CHUNK_REGEXP; // check the length of the string to work out if it should be returned or chunked - const chunkCount = Math.ceil(value.length / MAX_CHUNK_SIZE); + const chunkCount = Math.ceil(value.length / (chunkSize ?? MAX_CHUNK_SIZE)); if (chunkCount === 1) { return [{ name: key, value }]; diff --git a/packages/ssr/package.json b/packages/ssr/package.json index 5dd6da8c..146af8bd 100644 --- a/packages/ssr/package.json +++ b/packages/ssr/package.json @@ -12,7 +12,9 @@ ], "scripts": { "lint": "tsc", - "build": "tsup" + "build": "tsup", + "test": "vitest run", + "test:watch": "vitest" }, "repository": { "type": "git", @@ -41,7 +43,8 @@ "@types/cookie": "^0.5.1", "@types/ramda": "^0.29.3", "tsconfig": "workspace:*", - "tsup": "^6.7.0" + "tsup": "^6.7.0", + "vitest": "^0.34.6" }, "peerDependencies": { "@supabase/supabase-js": "^2.33.1" diff --git a/packages/ssr/src/utils/chunker.ts b/packages/ssr/src/utils/chunker.ts new file mode 100644 index 00000000..c72f2eb4 --- /dev/null +++ b/packages/ssr/src/utils/chunker.ts @@ -0,0 +1,94 @@ +interface Chunk { + name: string; + value: string; +} + +function createChunkRegExp(chunkSize: number) { + return new RegExp('.{1,' + chunkSize + '}', 'g'); +} + +const MAX_CHUNK_SIZE = 3600; +const MAX_CHUNK_REGEXP = createChunkRegExp(MAX_CHUNK_SIZE); + +/** + * create chunks from a string and return an array of object + */ +export function createChunks(key: string, value: string, chunkSize?: number): Chunk[] { + const re = chunkSize !== undefined ? createChunkRegExp(chunkSize) : MAX_CHUNK_REGEXP; + + // check the length of the string to work out if it should be returned or chunked + const chunkCount = Math.ceil(value.length / (chunkSize ?? MAX_CHUNK_SIZE)); + + if (chunkCount === 1) { + return [{ name: key, value }]; + } + + const chunks: Chunk[] = []; + // split string into a array based on the regex + const values = value.match(re); + values?.forEach((value, i) => { + const name: string = `${key}.${i}`; + chunks.push({ name, value }); + }); + + return chunks; +} + +// Get fully constructed chunks +export function combineChunks( + key: string, + retrieveChunk: (name: string) => string | null | undefined = () => { + return null; + } +) { + const value = retrieveChunk(key); + + // pkce code verifier + if (key.endsWith('-code-verifier') && value) { + return value; + } + + if (value) { + return value; + } + + let values: string[] = []; + for (let i = 0; ; i++) { + const chunkName = `${key}.${i}`; + const chunk = retrieveChunk(chunkName); + + if (!chunk) { + break; + } + + values.push(chunk); + } + + return values.length ? values.join('') : null; +} + +export function deleteChunks( + key: string, + retrieveChunk: (name: string) => string | null | undefined = () => { + return null; + }, + removeChunk: (name: string) => void = () => {} +) { + const value = retrieveChunk(key); + + if (value) { + removeChunk(key); + return; + } + + for (let i = 0; ; i++) { + const chunkName = `${key}.${i}`; + const chunk = retrieveChunk(chunkName); + + if (!chunk) { + break; + } + + removeChunk(chunkName); + } +} diff --git a/packages/ssr/src/utils/index.ts b/packages/ssr/src/utils/index.ts index 3e30adb0..47eb760b 100644 --- a/packages/ssr/src/utils/index.ts +++ b/packages/ssr/src/utils/index.ts @@ -1,2 +1,3 @@ export * from './helpers'; export * from './constants'; +export * from './chunker'; diff --git a/packages/ssr/tests/chunker.spec.ts b/packages/ssr/tests/chunker.spec.ts new file mode 100644 index 00000000..94ff2b50 --- /dev/null +++ b/packages/ssr/tests/chunker.spec.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from 'vitest'; +import { combineChunks, createChunks } from '../src/utils/chunker'; +import { CHUNK_STRING } from './helper'; + +describe('chunker', () => { + it('should not chunk and return one item', () => { + const chunked = createChunks('my-chunks', 'hello-world'); + expect(chunked.length).toBe(1); + }); + + it('should chunk and return two chunks', () => { + const chunked = createChunks('my-chunks', CHUNK_STRING, 2000); + const combined = combineChunks('my-chunks', (name) => { + let chunk = chunked.find((chunk) => { + return chunk.name === name; + }); + return chunk?.value; + }); + expect(chunked.length).toBe(2); + expect(combined).toBe(CHUNK_STRING); + }); + + it('should chunk and return twelve chunks', () => { + const chunked = createChunks('my-chunks', CHUNK_STRING, 320); + const combined = combineChunks('my-chunks', (name) => { + let chunk = chunked.find((chunk) => { + return chunk.name === name; + }); + return chunk?.value; + }); + expect(chunked.length).toBe(12); + expect(combined).toBe(CHUNK_STRING); + }); + + it('should chunk and return one hundred and one chunks', () => { + const chunked = createChunks('my-chunks', CHUNK_STRING, 36); + const combined = combineChunks('my-chunks', (name) => { + let chunk = chunked.find((chunk) => { + return chunk.name === name; + }); + return chunk?.value; + }); + expect(chunked.length).toBe(101); + expect(combined).toBe(CHUNK_STRING); + }); +}); diff --git a/packages/ssr/tests/helper.ts b/packages/ssr/tests/helper.ts new file mode 100644 index 00000000..a00365dc --- /dev/null +++ b/packages/ssr/tests/helper.ts @@ -0,0 +1,2 @@ +export const CHUNK_STRING = + 'zDq8KDAdv4PwF3UOp3mnEyx1xY71CaY4ZJdPTG8HpLHy3bCYs1x3vwPXdUqY75d0LYHL8KhxgrKqBEK531igiQNk1KqUKmMsabNlwcaF5E2gXA79vpwlxvi1wecwmKGVig4mJ0dzEEXKNsLgyQCsjKOpI7Nw2gnGAKKFdHle1SJeuFj9PyAHx9stMvQFpRQhoLRt3iI0uFA.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.lww9VHv5CCETYEFj9Q1emgiZI2nXZuvHbyJmIbYgMF9w533oG8ZEsdiKo81LAflQeUHYbbxOXTtroR2bdax0VYgjZ2qg5YeOnekqbgyDXwvmsOMktvg4x7JBJiHjzKK2kSPX0cCAhcU17ydRx19xq1gOUBup1j4kqEBbvKFKt67cfqZhT4kiERgbOt6mIyurSzUPgAhBNiqUJPM3872jVtweoum6mAfSgnG4H1qs2oNZjrbGi6Xv1H5WPALAdNzDwca0evrbbufUCKDX0XO27UTAFh9k4UFf0Dk1pPgKhomuWsfsJAJDvo2ZimmkXrlUo8OfihbpGCLMbEDRpxyIcIGTUQ30WeCyaHo2ds2hs2sh'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8e4ea0b3..4c8298dd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -425,6 +425,9 @@ importers: tsup: specifier: ^6.7.0 version: 6.7.0(typescript@4.9.5) + vitest: + specifier: ^0.34.6 + version: 0.34.6 packages/sveltekit: dependencies: @@ -5488,6 +5491,7 @@ packages: resolve: 1.22.4 transitivePeerDependencies: - supports-color + dev: true /eslint-import-resolver-node@0.3.9: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} @@ -5561,7 +5565,7 @@ packages: - eslint-import-resolver-webpack - supports-color - /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@2.7.1)(eslint@8.47.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.6.0)(eslint@8.47.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -5582,16 +5586,16 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.47.0)(typescript@5.2.2) + '@typescript-eslint/parser': 5.62.0(eslint@8.47.0)(typescript@4.9.5) debug: 3.2.7 eslint: 8.47.0 eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 2.7.1(eslint-plugin-import@2.28.1)(eslint@8.47.0) + eslint-import-resolver-typescript: 3.6.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.28.1)(eslint@8.47.0) transitivePeerDependencies: - supports-color - dev: false + dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.6.0)(eslint@8.47.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@2.7.1)(eslint@8.47.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -5612,14 +5616,14 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.47.0)(typescript@4.9.5) + '@typescript-eslint/parser': 5.62.0(eslint@8.47.0)(typescript@5.2.2) debug: 3.2.7 eslint: 8.47.0 - eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 3.6.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.28.1)(eslint@8.47.0) + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 2.7.1(eslint-plugin-import@2.28.1)(eslint@8.47.0) transitivePeerDependencies: - supports-color - dev: true + dev: false /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.0)(eslint@8.41.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} @@ -5650,6 +5654,36 @@ packages: transitivePeerDependencies: - supports-color + /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.0)(eslint@8.47.0): + resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 5.62.0(eslint@8.47.0)(typescript@4.9.5) + debug: 3.2.7 + eslint: 8.47.0 + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.6.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.28.1)(eslint@8.47.0) + transitivePeerDependencies: + - supports-color + dev: true + /eslint-plugin-es@3.0.1(eslint@8.47.0): resolution: {integrity: sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==} engines: {node: '>=8.10.0'} @@ -5679,8 +5713,8 @@ packages: debug: 3.2.7 doctrine: 2.1.0 eslint: 8.47.0 - eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@2.7.1)(eslint@8.47.0) + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@2.7.1)(eslint@8.47.0) has: 1.0.3 is-core-module: 2.13.0 is-glob: 4.0.3 @@ -5748,8 +5782,8 @@ packages: debug: 3.2.7 doctrine: 2.1.0 eslint: 8.47.0 - eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.6.0)(eslint@8.47.0) + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.0)(eslint@8.47.0) has: 1.0.3 is-core-module: 2.13.0 is-glob: 4.0.3