Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/workflows/test-and-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Test and Publish

on:
push:
branches:
- master
pull_request:
branches:
- master
release:
types: [created]

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14, 16]
steps:
- uses: actions/checkout@v3

- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'yarn'

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Run tests
run: yarn test

publish:
needs: test
runs-on: ubuntu-latest
if: github.event_name == 'release' && github.event.action == 'created'
steps:
- uses: actions/checkout@v3

- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
registry-url: 'https://registry.npmjs.org'
cache: 'yarn'

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Build
run: yarn build

- name: Publish to NPM
run: yarn publish --access public --non-interactive
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
node_modules
dist
package-lock.json

.idea
Comment on lines 1 to 5

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignoring package-lock.json is usually counterproductive for libraries/apps that want reproducible installs and deterministic CI. If this repo uses npm, the lockfile should typically be committed rather than ignored.

If you intentionally don’t want lockfiles, consider also ignoring yarn.lock/pnpm-lock.yaml for consistency and documenting the policy in the README.

Suggestion

Decide on a lockfile policy:

  • If using npm: remove package-lock.json from .gitignore and commit it.
  • If intentionally lockfile-free: document it and ignore all lockfiles consistently.

Reply with "@CharlieHelps yes please" if you’d like me to add a commit reverting the .gitignore change (and optionally adding a short note to the README about lockfile policy).

11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,19 @@ Easily use TinyURL in your browser.
import shortenUrl from "@kulkul/tinyurl-client";

shortenUrl("https://kulkul.tech").then((result) => {
console.log({ result }); // https://tinyurl.com/<slug>
console.log({ result }); // https://tinyurl.com/<random-slug>
});
```

To use it using alias you can do the following
## Important Note on Custom Aliases

**Custom aliases are no longer supported.** The underlying TinyURL API endpoint used by this client does not support custom aliases. Any alias parameter provided will be ignored, and a random slug will be generated instead.

```javascript
import shortenUrl from "@kulkul/tinyurl-client";

shortenUrl("https://kulkul.tech", "shorted-kulkul").then((result) => {
console.log({ result }); // https://tinyurl.com/shorted-kulkul
// Alias parameter is deprecated and will be ignored
shortenUrl("https://kulkul.tech", "my-custom-alias").then((result) => {
console.log({ result }); // https://tinyurl.com/<random-slug> (NOT my-custom-alias)
});
```
43 changes: 34 additions & 9 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,40 @@
import * as axios from "axios";

const shortenUrl = async (url, alias = "") => {
Comment on lines 2 to 3

Copilot AI Jan 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No input validation for the URL parameter. The function doesn't validate whether the 'url' parameter is a valid URL, is empty, or is undefined before sending it to the API. This could lead to unnecessary API calls with invalid data. Consider adding basic validation to check if the URL is a non-empty string and optionally if it's a valid URL format.

Suggested change
const shortenUrl = async (url, alias = "") => {
const validateUrl = (url) => {
if (typeof url !== "string" || url.trim() === "") {
throw new TypeError("The 'url' parameter must be a non-empty string.");
}
try {
// This will throw if the URL is not valid.
new URL(url);
} catch {
throw new TypeError("The 'url' parameter must be a valid URL.");
}
};
const shortenUrl = async (url, alias = "") => {
validateUrl(url);

Copilot uses AI. Check for mistakes.

Copilot AI Jan 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README.md documentation still shows that the alias parameter works and will produce a custom alias (line 31: "// https://tinyurl.com/shorted-kulkul"), but the PR description states that alias support has been lost. The documentation should be updated to reflect that the alias parameter is deprecated and no longer functional, or removed entirely to avoid confusing users.

Copilot uses AI. Check for mistakes.
const encodedUrl = encodeURI(url);
const response = await axios.get(
`https://cors-anywhere.herokuapp.com/https://tinyurl.com/create.php?source=indexpage&url=${encodedUrl}&alias=${alias}`,
{ headers: { 'X-Requested-With': 'XMLHttpRequest' } }
);

const el = document.createElement("html");
el.innerHTML = response.data;
return el.querySelector("#copy_div").href;
// Validate input
if (typeof url !== "string" || url.trim() === "") {
throw new TypeError("The 'url' parameter must be a non-empty string.");
}

if (alias) {
console.warn(
"TinyURL alias parameter is not supported when using tinyurl-rest-wrapper. " +
"The alias will be ignored and a random slug will be generated instead."
);
}
Comment on lines 3 to +14

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shortenUrl() now emits console.warn() whenever an alias is passed. For a library function, this creates unavoidable side effects for consumers (noise in tests/CI logs, potentially breaking apps that treat warnings as failures).

Given alias is intentionally ignored, the more appropriate path is usually either:

  • soft-deprecate without logging (document it in README/CHANGELOG), or
  • provide an opt-in way to warn (e.g., options: { onAliasIgnored } or silent: true), or
  • warn only once per process (to limit log spam).

Right now, calling shortenUrl(url, alias) in a loop could flood logs.

Suggestion

Consider removing the unconditional console.warn() and replacing it with an opt-in/once-only mechanism.

Example (warn once):

let warnedAliasIgnored = false;

const shortenUrl = async (url, alias = "") => {
  if (alias && !warnedAliasIgnored) {
    warnedAliasIgnored = true;
    console.warn(
      "TinyURL alias parameter is not supported when using tinyurl-rest-wrapper. " +
        "The alias will be ignored and a random slug will be generated instead."
    );
  }
  // ...
};

Or accept an options object:

const shortenUrl = async (url, alias = "", { warnOnAlias = false } = {}) => {
  if (alias && warnOnAlias) console.warn(/* ... */);
  // ...
};

Reply with "@CharlieHelps yes please" if you’d like me to add a commit implementing the once-only warning (or the options-based approach).


try {
const response = await axios.post(
"https://tinyurl-rest-wrapper.onrender.com/",
{ url },
{ headers: { "Content-Type": "application/json" } }
);

// Validate response structure
if (!response.data || typeof response.data.tinyurl !== "string") {
throw new Error("Invalid response from TinyURL service.");
}

return response.data.tinyurl;
} catch (error) {
// Re-throw with more context if it's not already a TypeError
if (error instanceof TypeError) {
throw error;
}
throw new Error(
`Failed to shorten URL: ${error.message || "Unknown error"}`
);
}
};

export default shortenUrl;
Loading