{"id":20440803,"url":"https://github.com/jsejcksn/neocities-deno","last_synced_at":"2026-02-02T05:01:50.613Z","repository":{"id":44624662,"uuid":"453242579","full_name":"jsejcksn/neocities-deno","owner":"jsejcksn","description":"Neocities.org API client: written in TypeScript for use with Deno","archived":false,"fork":false,"pushed_at":"2022-02-09T22:03:49.000Z","size":27,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-11-03T20:17:22.120Z","etag":null,"topics":["deno","neocities"],"latest_commit_sha":null,"homepage":"https://deno.land/x/neocities","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jsejcksn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-01-28T23:06:06.000Z","updated_at":"2025-04-17T21:56:41.000Z","dependencies_parsed_at":"2022-07-20T20:34:39.481Z","dependency_job_id":null,"html_url":"https://github.com/jsejcksn/neocities-deno","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/jsejcksn/neocities-deno","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsejcksn%2Fneocities-deno","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsejcksn%2Fneocities-deno/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsejcksn%2Fneocities-deno/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsejcksn%2Fneocities-deno/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jsejcksn","download_url":"https://codeload.github.com/jsejcksn/neocities-deno/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsejcksn%2Fneocities-deno/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29006081,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-02T04:25:24.522Z","status":"ssl_error","status_checked_at":"2026-02-02T04:24:51.069Z","response_time":58,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deno","neocities"],"created_at":"2024-11-15T09:26:36.625Z","updated_at":"2026-02-02T05:01:50.563Z","avatar_url":"https://github.com/jsejcksn.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# neocities-deno\n\n[Neocities.org API](https://neocities.org/api) client: written in TypeScript for use with Deno\n\n\u003e Generated type documentation is available at [doc.deno.land](https://doc.deno.land/https://deno.land/x/neocities/mod.ts).\n\n\n## API keys\n\nUsing this module requires an API key (also called a token) for your account. It's needed to access your account information via the API, so that you can do useful things in your script.\n\n\u003e You can read more about tokens by navigating to your [account settings](https://neocities.org/settings), then select \"Manage Site Settings\", then select \"API Key\".\n\nIf you haven't already created a token for your account, you can do so by using the `get_token.ts` script. It will ask for your username and password, and then use the API to create a token for you. Once you have your token, you can use it with this module. Here's what it looks like to use the `get_token.ts` script:\n\n```\n$ deno run https://deno.land/x/neocities@v0.1.0/get_token.ts\nProvide your neocities username and password to obtain your API key:\n⚠️  ️Deno requests network access to \"neocities.org\". Allow? [y/n (y = yes allow, n = no deny)] y\nusername jsejcksn\npassword my_very_secret_actually_strong_password\nYour neocities API key (token) is:\n3287ea7b1960458d8fa1a33f73bf3eb5\n```\n\n\n## How to use\n\n\n### Setup and running in the console\n\nYou can try the following examples. First, create a TypeScript module somewhere on your device: let's say you create an example module at `/neocities-example/example.ts`. This is how you can run your example with the [permissions](https://deno.land/manual@v1.18.1/getting_started/permissions) that it needs:\n\n```\n# cd to the directory where you created the TypeScript module\n$ cd /neocities-example\n\n# Set any environment variables that you want to use\n$ export NEOCITIES_USERNAME=your_actual_username\n$ export NEOCITIES_PASSWORD=your_very_secret_actually_strong_password\n\n# Including your API key (token) if you already have it:\n$ export NEOCITIES_TOKEN=your_actual_neocities_token\n\n# Run the example module script with the permissions it needs\n$ deno run --allow-env=NEOCITIES_USERNAME,NEOCITIES_PASSWORD,NEOCITIES_TOKEN --allow-net=neocities.org --allow-read example.ts\n```\n\n\n### Create an API client\n\n```ts\nimport {assert} from 'https://deno.land/std@0.123.0/testing/asserts.ts';\nimport {NeocitiesAPI} from 'https://deno.land/x/neocities@v0.1.0/mod.ts';\n\n// Create an API client from a token\n\nconst token = Deno.env.get('NEOCITIES_TOKEN'); // \"3287ea7b1960458d8fa1a33f73bf3eb5\"\nassert(token, 'Token not found');\n\nconst api = new NeocitiesAPI(token);\n```\n\n```ts\n// If you haven't created/saved a token yet, that's ok: you can use your username + password\n// This example gets them from environment variables: don't store them in your source code 😅\n\nconst username = Deno.env.get('NEOCITIES_USERNAME'); // \"jsejcksn\"\nassert(username, 'Username not found');\n\nconst password = Deno.env.get('NEOCITIES_PASSWORD'); // \"my_very_secret_actually_strong_password\"\nassert(password, 'Password not found');\n\nconst api = await NeocitiesAPI.createFromCredentials(username, password);\n```\n\n\n### Get site info\n\n```ts\n// Get info about your site\n\nconst infoResult = await api.info();\nconsole.log(infoResult);\n```\n\n\u003e ```\n\u003e {\n\u003e   result: \"success\",\n\u003e   info: {\n\u003e     sitename: \"jsejcksn\",\n\u003e     views: 9,\n\u003e     hits: 22,\n\u003e     created_at: 2022-01-28T13:11:09.000Z,\n\u003e     last_updated: 2022-01-31T00:04:03.000Z,\n\u003e     domain: null,\n\u003e     tags: [],\n\u003e     latest_ipfs_hash: null\n\u003e   }\n\u003e }\n\u003e ```\n\n```ts\n// Get info about the named site\n\nconst infoResult = await api.info('kyledrake');\nconsole.log(infoResult);\n```\n\n\u003e ```\n\u003e {\n\u003e   result: \"success\",\n\u003e   info: {\n\u003e     sitename: \"kyledrake\",\n\u003e     views: 874044,\n\u003e     hits: 1072518,\n\u003e     created_at: 2013-06-03T06:45:02.000Z,\n\u003e     last_updated: 2022-01-06T20:22:34.000Z,\n\u003e     domain: \"kyledrake.com\",\n\u003e     tags: [ \"personal\" ],\n\u003e     latest_ipfs_hash: \"bafybeicmk3wkw5vqtdzaasybcfuhed3wqtqcfy7u5mfcwy7w2rk5lels4i\"\n\u003e   }\n\u003e }\n\u003e ```\n\n\n### List files\n\n```ts\n// Get a list of all files for your site\n\nconst listResult = await api.list();\nconsole.log(listResult);\n```\n\n\u003e ```\n\u003e {\n\u003e   result: \"success\",\n\u003e   files: [\n\u003e     {\n\u003e       path: \"index.html\",\n\u003e       is_directory: false,\n\u003e       size: 1083,\n\u003e       updated_at: 2022-01-28T13:11:09.000Z,\n\u003e       sha1_hash: \"99bc354d84057d6900a71d0cb5e8dde069d8689e\"\n\u003e     },\n\u003e     {\n\u003e       path: \"neocities.png\",\n\u003e       is_directory: false,\n\u003e       size: 13232,\n\u003e       updated_at: 2022-01-28T13:11:09.000Z,\n\u003e       sha1_hash: \"fd2ee41b1922a39a716cacb88c323d613b0955e4\"\n\u003e     },\n\u003e     {\n\u003e       path: \"not_found.html\",\n\u003e       is_directory: false,\n\u003e       size: 347,\n\u003e       updated_at: 2022-01-28T13:11:09.000Z,\n\u003e       sha1_hash: \"d7f004e9d3b2eaaa8827f741356f1122dc9eb030\"\n\u003e     },\n\u003e     {\n\u003e       path: \"style.css\",\n\u003e       is_directory: false,\n\u003e       size: 298,\n\u003e       updated_at: 2022-01-28T13:11:09.000Z,\n\u003e       sha1_hash: \"e516457acdb0d00710ab62cc257109ef67209ce8\"\n\u003e     },\n\u003e     { path: \"test\", is_directory: true, updated_at: 2022-01-31T22:14:11.000Z },\n\u003e     {\n\u003e       path: \"test/text_file1.txt\",\n\u003e       is_directory: false,\n\u003e       size: 9,\n\u003e       updated_at: 2022-01-31T22:14:56.000Z,\n\u003e       sha1_hash: \"02d92c580d4ede6c80a878bdd9f3142d8f757be8\"\n\u003e     }\n\u003e   ]\n\u003e }\n\u003e ```\n\n```ts\n// Get a list of files in the provided directory path in your site\n\nconst listResult = await api.list('test');\nconsole.log(listResult);\n```\n\n\u003e ```\n\u003e {\n\u003e   result: \"success\",\n\u003e   files: [\n\u003e     {\n\u003e       path: \"test/text_file1.txt\",\n\u003e       is_directory: false,\n\u003e       size: 9,\n\u003e       updated_at: 2022-01-31T22:14:56.000Z,\n\u003e       sha1_hash: null\n\u003e     }\n\u003e   ]\n\u003e }\n\u003e ```\n\n\n### Upload files\n\n\u003e You can read more about [which file types can be uploaded](https://neocities.org/site_files/allowed_types).\n\nLet's say you create a text file next to your example script, and you name it `hello-world.txt` with this as the contents:\n\n```\nhello world\n\n```\n\nYou can upload it by referencing its local path on your device, and the path to where you want to upload it on your site. (You can do this for multiple files at once.)\n\n```ts\n// Upload one or more files by local path\n\nimport {type UploadableFile} from 'https://deno.land/x/neocities@v0.1.0/mod.ts';\n\nconst files: UploadableFile[] = [\n  {\n    localPath: './hello-world.txt',\n    uploadPath: 'test/hello.txt',\n  },\n];\n\nconst uploadResult = await api.upload(files);\nconsole.log(uploadResult);\n```\n\n\u003e ```\n\u003e { result: \"success\", message: \"your file(s) have been successfully uploaded\" }\n\u003e ```\n\nYou can also upload raw file data if you want to upload something that doesn't exist as a file on your device. Here's an example of uploading a text file that's created in the script:\n\n```ts\n// Upload one or more files as raw data\n\nimport {type UploadableFile} from 'https://deno.land/x/neocities@v0.1.0/mod.ts';\n\nconst hello2FileData = `hello other planets, too!`;\n\nconst files: UploadableFile[] = [\n  {\n    data: hello2FileData,\n    uploadPath: 'test/hello2.txt',\n  },\n];\n\nconst uploadResult = await api.upload(files);\nconsole.log(uploadResult);\n```\n\n\u003e ```\n\u003e { result: \"success\", message: \"your file(s) have been successfully uploaded\" }\n\u003e ```\n\n\u003e You can even include a mix of both types of files in the files array:\n\u003e - files on your device, using local paths\n\u003e - files created from raw data\n\nEven though the responses indicated that the uploads were successful, you can check to see the changes by using the `list` method from above with the `\"test\"` directory path that they were uploaded to:\n\n```ts\nconst listResult = await api.list('test');\nconsole.log(listResult);\n```\n\n\u003e ```\n\u003e {\n\u003e   result: \"success\",\n\u003e   files: [\n\u003e     {\n\u003e       path: \"test/hello.txt\",\n\u003e       is_directory: false,\n\u003e       size: 12,\n\u003e       updated_at: 2022-01-31T22:44:53.000Z,\n\u003e       sha1_hash: null\n\u003e     },\n\u003e     {\n\u003e       path: \"test/hello2.txt\",\n\u003e       is_directory: false,\n\u003e       size: 25,\n\u003e       updated_at: 2022-01-31T22:51:05.000Z,\n\u003e       sha1_hash: null\n\u003e     },\n\u003e     {\n\u003e       path: \"test/text_file1.txt\",\n\u003e       is_directory: false,\n\u003e       size: 9,\n\u003e       updated_at: 2022-01-31T22:14:56.000Z,\n\u003e       sha1_hash: null\n\u003e     }\n\u003e   ]\n\u003e }\n\u003e ```\n\n\n### Delete files\n\n\u003e **Be careful with this API method.** There is no way to undo a delete!\n\nYou can delete the two files that were uploaded in the last step:\n\n```ts\n// Delete one or more paths (files/directories)\n\nconst filePathsToDelete = [\n  'test/hello.txt',\n  'test/hello2.txt',\n];\n\nconst deleteResult = await api.delete(filePathsToDelete);\nconsole.log(deleteResult);\n```\n\n\u003e ```\n\u003e { result: \"success\", message: \"file(s) have been deleted\" }\n\u003e ```\n\nConfirming that they are no longer in the `\"test\"` folder using the `list` method:\n\n```ts\nconsole.log(await api.list('test'));\n```\n\n\u003e ```\n\u003e {\n\u003e   result: \"success\",\n\u003e   files: [\n\u003e     {\n\u003e       path: \"test/text_file1.txt\",\n\u003e       is_directory: false,\n\u003e       size: 9,\n\u003e       updated_at: 2022-01-31T22:14:56.000Z,\n\u003e       sha1_hash: null\n\u003e     }\n\u003e   ]\n\u003e }\n\u003e ```\n\n\u003e You can also delete an entire directory at once, instead of specifying lots of files to delete. Again, be careful!\n\n\n## Functional programming\n\nAll of the examples above used an instance of the `NeocitiesAPI` stateful class, created from an API token which was used implicitly in each method call.\n\nIf you prefer a functional approach, you can use the functional form of each of the class methods, which are also exported:\n\n\u003e If you review the source code, you'll see that the class actually just uses these functions:\n\n```ts\nimport {\n  deleteFiles,\n  getSiteInfo,\n  getToken,\n  listFiles,\n  uploadFiles,\n  type UploadableFile,\n} from 'https://deno.land/x/neocities@v0.1.0/mod.ts';\n\n// declare const username: string;\n// declare const password: string;\n\n// get a token using credentials\nconst token = await getToken(username, password);\n\n// All of the functional forms accept a token as the first argument,\n// otherwise, they are identical to the class methods:\n\n// info\nlet infoResult = await getSiteInfo(token);\ninfoResult = await getSiteInfo(token, 'kyledrake');\n\n// list\nlet listResult = await listFiles(token);\nlistResult = await listFiles(token, 'test');\n\n// upload\nconst rawFileData = 'hello everyone';\nconst uploadPath = 'test/hello-everyone.txt';\nconst files: UploadableFile[] = [{data: rawFileData, uploadPath}];\nconst uploadResult = await uploadFiles(token, files);\n\n// delete\nconst filePathsToDelete = files.map(({uploadPath}) =\u003e uploadPath);\nconst deleteResult = await deleteFiles(token, filePathsToDelete);\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsejcksn%2Fneocities-deno","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjsejcksn%2Fneocities-deno","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsejcksn%2Fneocities-deno/lists"}