diff --git a/index.js b/index.js index aae4639..fddccf8 100644 --- a/index.js +++ b/index.js @@ -80,10 +80,18 @@ class Replicate { * @returns {Promise} - Resolves with the output of running the model */ async run(identifier, options) { - const pattern = - /^(?[a-zA-Z0-9-_]+?)\/(?[a-zA-Z0-9-_]+?):(?[0-9a-fA-F]+)$/; - const match = identifier.match(pattern); + // Define a pattern for owner and model names that allows + // letters, digits, and certain special characters. + // Example: "user123", "abc__123", "user.name" + const namePattern = /[a-zA-Z0-9]+(?:(?:[._]|__|[-]*)[a-zA-Z0-9]+)*/; + + // Define a pattern for "owner/name:version" format with named capturing groups. + // Example: "user123/repo_a:1a2b3c" + const pattern = new RegExp( + `^(?${namePattern.source})/(?${namePattern.source}):(?[0-9a-fA-F]+)$` + ); + const match = identifier.match(pattern); if (!match || !match.groups) { throw new Error( 'Invalid version. It must be in the format "owner/name:version"' diff --git a/index.test.ts b/index.test.ts index 89bcc8d..2b239a9 100644 --- a/index.test.ts +++ b/index.test.ts @@ -391,6 +391,37 @@ describe('Replicate client', () => { ); expect(output).toBe('foobar'); }); + + test('Does not throw an error for identifier containing hyphen and full stop', async () => { + nock(BASE_URL) + .post('/predictions') + .reply(200, { + id: 'ufawqhfynnddngldkgtslldrkq', + status: 'processing', + }) + .get('/predictions/ufawqhfynnddngldkgtslldrkq') + .reply(200, { + id: 'ufawqhfynnddngldkgtslldrkq', + status: 'succeeded', + output: 'foobar', + }); + + await expect(client.run('a/b-1.0:abc123', { input: { text: 'Hello, world!' } })).resolves.not.toThrow(); + }); + + test('Throws an error for invalid identifiers', async () => { + const options = { input: { text: 'Hello, world!' } } + + await expect(client.run('owner/model:invalid', options)).rejects.toThrow(); + + // @ts-expect-error + await expect(client.run('owner:abc123', options)).rejects.toThrow(); + + await expect(client.run('/model:abc123', options)).rejects.toThrow(); + + // @ts-expect-error + await expect(client.run(':abc123', options)).rejects.toThrow(); + }); }); // Continue with tests for other methods