{"id":41857098,"url":"https://github.com/neovici/cosmoz-queue","last_synced_at":"2026-03-12T09:51:17.180Z","repository":{"id":332925939,"uuid":"1114970569","full_name":"Neovici/cosmoz-queue","owner":"Neovici","description":"Cosmoz Queue to manage a list of entitites","archived":false,"fork":false,"pushed_at":"2026-01-23T12:41:01.000Z","size":2006,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-24T04:38:00.637Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Neovici.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-12T06:42:01.000Z","updated_at":"2026-01-23T12:40:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Neovici/cosmoz-queue","commit_stats":null,"previous_names":["neovici/cosmoz-queue"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Neovici/cosmoz-queue","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Neovici%2Fcosmoz-queue","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Neovici%2Fcosmoz-queue/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Neovici%2Fcosmoz-queue/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Neovici%2Fcosmoz-queue/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Neovici","download_url":"https://codeload.github.com/Neovici/cosmoz-queue/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Neovici%2Fcosmoz-queue/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28752377,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-25T10:25:12.305Z","status":"ssl_error","status_checked_at":"2026-01-25T10:25:11.933Z","response_time":113,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5: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":[],"created_at":"2026-01-25T11:02:46.686Z","updated_at":"2026-03-12T09:51:17.173Z","avatar_url":"https://github.com/Neovici.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @neovici/cosmoz-queue\n\nA hooks-based UI component library for building **master-detail views** with list, split, and queue navigation modes. Built on [`@pionjs/pion`](https://github.com/pionjs/pion) and [`lit-html`](https://lit.dev/docs/libraries/standalone-templates/).\n\n\u003e **What is a \"queue\"?** Not a data structure -- a UI pattern. Users work through a list of items one-by-one, like processing a queue of tasks. The component provides three view modes: a data table, a side-by-side split, and a full-screen sequential navigator.\n\n## Installation\n\n```sh\nnpm install @neovici/cosmoz-queue\n```\n\nPeer dependencies: `@pionjs/pion`, `lit-html`, `i18next`.\n\n## Quick start\n\nThe `queue()` factory is the recommended high-level API. It composes all the internal hooks and renders the complete queue UI -- tabs, navigation, list, and detail view.\n\n```ts\nimport { queue } from '@neovici/cosmoz-queue';\nimport { spread } from '@open-wc/lit-helpers';\nimport { component, useCallback } from '@pionjs/pion';\nimport { html } from 'lit-html';\nimport { ref } from 'lit-html/directives/ref.js';\nimport { fetchOrderDetails$ } from './api';\nimport type { OrderListItem } from './types';\n\nconst OrderListQueue = ({ heading }: { heading: string }) =\u003e\n  queue\u003cOrderListItem\u003e({\n    // Title displayed above the list\n    heading,\n\n    // Unique key for persisting table column settings\n    settingsId: 'order-list',\n\n    // URL hash parameter names (enables deep-linking and back/forward)\n    idHashParam: 'id',\n    tabHashParam: 'qtab',\n\n    // Async function that fetches full details for a list item.\n    // Results are memoized per item identity.\n    details: useCallback(\n      (item: OrderListItem) =\u003e fetchOrderDetails$(item.id),\n      [],\n    ),\n\n    // Render the list component. `thru` contains bindings that wire\n    // the omnitable events to queue state (items, selection, clicks).\n    // Spread them onto your list component.\n    list: (thru, { onRef }) =\u003e\n      html`\u003corder-list-core ${spread(thru)} ${ref(onRef)}\u003e\u003c/order-list-core\u003e`,\n\n    // Render the detail view. `nav` contains prev/next buttons --\n    // place them in a slot or wherever navigation belongs in your view.\n    view: (thru, { nav }) =\u003e\n      html`\u003corder-view-core ${spread(thru)}\u003e${nav}\u003c/order-view-core\u003e`,\n\n    // Render a loading skeleton shown while `details` resolves.\n    loader: (thru, { nav }) =\u003e\n      html`\u003corder-view-skeleton ${spread(thru)}\u003e${nav}\u003c/order-view-skeleton\u003e`,\n  });\n\ncustomElements.define('order-list-queue', component(OrderListQueue));\n```\n\n### `queue()` props\n\n| Prop | Type | Default | Description |\n|---|---|---|---|\n| `heading` | `string` | -- | Title displayed above the tabs |\n| `settingsId` | `string?` | -- | Key for persisting omnitable column settings |\n| `idHashParam` | `string` | `'qid'` | URL hash param for the selected item ID |\n| `tabHashParam` | `string` | `'qtab'` | URL hash param for the active tab |\n| `details` | `(item: I) =\u003e PromiseLike\u003cD\u003e` | -- | Fetches full details for a list item |\n| `api` | `(id: string, item: I) =\u003e string` | -- | Alternative to `details`: returns a URL, queue fetches JSON internally |\n| `list` | `(thru, props) =\u003e TemplateResult` | -- | Renders the list component |\n| `view` | `(thru, props) =\u003e TemplateResult` | -- | Renders the detail view |\n| `loader` | `(thru, props) =\u003e TemplateResult` | -- | Renders the loading skeleton |\n| `pagination` | `Pagination?` | -- | Server-side pagination config |\n| `fallback` | `string?` | -- | Default tab when none is set in URL hash |\n| `split` | `SplitOpts?` | -- | Split.js configuration (sizes, min sizes) |\n| `afterHeading` | `unknown?` | -- | Extra content rendered after the heading |\n\nUse **`details`** when you control the fetch (most common). Use **`api`** when you want the queue to handle fetching via URL.\n\n### The `thru` bindings\n\nThe `list`, `view`, and `loader` render functions receive a `thru` object as their first argument. This object contains property and event bindings that wire the child component to queue state:\n\n**For `list`:**\n- `.settingsId`, `.exposedParts` -- table configuration\n- `@visible-items-changed`, `@selected-items-changed`, `@total-available-changed` -- sync items/selection back to queue\n- `@omnitable-item-click` -- item click handler\n- `@async-simple-action` -- action completion handler\n\n**For `view`:**\n- `.item` -- the current detail item (list item or resolved detail)\n- `.hideActions` -- whether to hide actions (true when items are selected in split mode)\n- `@async-simple-action` -- action completion handler\n\nSpread these onto your component with `${spread(thru)}` from `@open-wc/lit-helpers`.\n\n## View modes\n\nThe queue provides three tab-based view modes:\n\n| Mode | Tab name | Behavior |\n|---|---|---|\n| **List** | `overview` | Full-width data table (`cosmoz-omnitable`). The default view. |\n| **Split** | `split` | Side-by-side: resizable list on the left, detail view on the right. Disabled on mobile. |\n| **Queue** | `queue` | Full-screen detail view with prev/next navigation and keyboard arrow keys. |\n\nThe active tab is synced to the URL hash (e.g. `#qtab=split\u0026id=abc-123`), enabling deep-linking and browser back/forward navigation.\n\nOn mobile viewports, the split tab is disabled and falls back to queue mode automatically.\n\n## List module\n\nThe `@neovici/cosmoz-queue/list` entry point provides a managed `cosmoz-omnitable` with built-in state management, data fetching, pagination, and action rendering.\n\n### `listCore()`\n\nThe high-level factory that combines `useListCore()` + `renderListCore()`:\n\n```ts\nimport { itemClick } from '@neovici/cosmoz-queue';\nimport { column, listCore, style } from '@neovici/cosmoz-queue/list';\nimport { component, html } from '@pionjs/pion';\nimport { t } from 'i18next';\nimport type { OrderListItem } from './types';\n\ninterface Props {\n  exposedParts: string;\n  api: (params: { pathLocator: string }) =\u003e Promise\u003c{\n    items: OrderListItem[];\n    metaData: { totalAvailable: number };\n  }\u003e;\n}\n\nconst OrderListCore = ({ exposedParts, api }: Props) =\u003e {\n  return listCore({\n    // Unique key for persisting column settings\n    settingsId: 'order-list-core',\n\n    // CSS parts to expose for external styling\n    exposedParts,\n\n    // When false, omnitable applies its own local filtering\n    noLocal: false,\n\n    // Column definitions. The tuple is [factory, deps] -- same pattern\n    // as useMemo. The factory returns an object where each key becomes\n    // a column name.\n    columns: [\n      () =\u003e ({\n        title: column({\n          render: ({ name }) =\u003e\n            html`\u003ccosmoz-omnitable-column\n              name=\"${name}\"\n              title=${t('Title')}\n              flex=\"2\"\n              .renderCell=${(\n                _col: unknown,\n                { item, index }: { item: OrderListItem; index: number },\n              ) =\u003e\n                html`\u003ca\n                  @click=\"${itemClick({\n                    index,\n                    activate: ['split', 'queue'],\n                  })}\"\n                  \u003e${item.title}\u003c/a\n                \u003e`}\n            \u003e\u003c/cosmoz-omnitable-column\u003e`,\n        }),\n\n        status: column({\n          render: ({ name }) =\u003e\n            html`\u003ccosmoz-omnitable-column-autocomplete\n              name=\"${name}\"\n              title=${t('Status')}\n              value-path=\"status.name\"\n            \u003e\u003c/cosmoz-omnitable-column-autocomplete\u003e`,\n        }),\n\n        createdAt: column({\n          render: ({ name }) =\u003e\n            html`\u003ccosmoz-omnitable-column-date\n              name=\"${name}\"\n              title=${t('Date created')}\n            \u003e\u003c/cosmoz-omnitable-column-date\u003e`,\n        }),\n      }),\n      [], // dependency array (empty = compute once)\n    ],\n\n    // Reactive query parameters. Recomputed when deps change,\n    // which triggers a new data fetch.\n    params: [() =\u003e ({ pathLocator: '/some/path' }), []],\n\n    // Data fetcher. Receives { params, page, pageSize }.\n    // Must return { items, total }.\n    list$: [\n      ({ params }) =\u003e\n        api(params).then((r) =\u003e ({\n          items: r.items ?? [],\n          total: r.metaData?.totalAvailable ?? 0,\n        })),\n      [api],\n    ],\n\n    // Batch actions shown when items are selected\n    actions: [myAction()],\n  });\n};\n\ncustomElements.define(\n  'order-list-core',\n  component(OrderListCore, { styleSheets: [style] }),\n);\n```\n\n### `listCore()` props\n\n| Prop | Type | Description |\n|---|---|---|\n| `settingsId` | `string` | Persistence key for column settings |\n| `exposedParts` | `string?` | CSS `exportparts` value |\n| `noLocal` | `boolean` | Skip omnitable local filtering (default: `true`) |\n| `columns` | `[() =\u003e Columns, deps[]]` | Column definitions (memoized) |\n| `params` | `[(opts) =\u003e Params, deps[]]` | Query parameters (memoized). The `opts` include `{ filters, descending, sortOn, columns }` |\n| `list$` | `[(props) =\u003e Promise\u003c{ items, total }\u003e, deps[]]` | Data fetcher. Receives `{ params, page, pageSize }` |\n| `pageSize` | `number?` | Items per page for \"load more\" (default: `50`) |\n| `actions` | `Action[]?` | Batch actions |\n| `content` | `(opts) =\u003e Renderable` | Extra content inside the omnitable |\n| `hashParam` | `string?` | URL hash param for omnitable state |\n| `csvFilename` | `string?` | Filename for CSV export |\n| `enabledColumns` | `string[]?` | Initially visible columns |\n\n### `column()`\n\nType-safe column definition helper:\n\n```ts\nimport { column } from '@neovici/cosmoz-queue/list';\n\nconst myColumn = column({\n  // Optional ordering hint\n  order: 1,\n\n  // Optional sort key\n  sort: 'name',\n\n  // Optional filter transform: receives the raw filter value,\n  // returns the value sent to the API\n  filter: (value: string) =\u003e ({ name: value }),\n\n  // Render function -- receives { name } where name is the object key\n  render: ({ name }) =\u003e\n    html`\u003ccosmoz-omnitable-column\n      name=\"${name}\"\n      title=\"Name\"\n    \u003e\u003c/cosmoz-omnitable-column\u003e`,\n});\n```\n\n### `useListCore()` / `useListCoreState()`\n\nFor cases where you need more control over the list without `renderListCore()`:\n\n- **`useListCore(props)`** -- manages columns, params, data fetching, pagination, and form dialog state. Returns `{ data$, columns, loadMore, dialog, open, ...state }`.\n- **`useListCoreState(defaults?)`** -- lower-level state: `filters`, `sortOn`, `descending`, `groupOn`, `selectedItems`, plus their setters and `setTotalAvailable`.\n\n## Actions module\n\nThe `@neovici/cosmoz-queue/actions` entry point provides a declarative system for defining user actions that open form dialogs.\n\n### Defining an action\n\n```ts\nimport { action, Action, defaultButton } from '@neovici/cosmoz-queue/actions';\nimport { t } from 'i18next';\nimport { when } from 'lit-html/directives/when.js';\n\n// action() is an identity function that provides type inference\nexport const approveOrder = action\u003cOrderItem, ApproveFields\u003e({\n  // Label shown on the button\n  title: () =\u003e t('Approve'),\n\n  // Optional: filter which items this action applies to.\n  // If provided, non-applicable items are excluded and the button\n  // shows a count badge like \"Approve (3/5)\".\n  applicable: (item) =\u003e item.status === 'pending',\n\n  // Optional: custom button renderer. Defaults to `defaultButton()`.\n  // Return `nothing` to hide the button conditionally.\n  button: (opts) =\u003e\n    when(opts.items.length \u003e= 1, () =\u003e defaultButton(opts)),\n\n  // Dialog configuration. Opens a `cosmoz-form` dialog.\n  // `items` contains only the applicable items.\n  dialog: ({ items, title }) =\u003e ({\n    heading: title,\n    description: t('Approve the selected orders'),\n    fields: [\n      // cosmoz-form field definitions\n    ],\n    initial: {},\n    onSave: async (values) =\u003e {\n      await approveOrders(items.map((i) =\u003e i.id), values);\n    },\n  }),\n});\n```\n\n### Rendering actions\n\nActions are rendered automatically by `listCore()` when passed as the `actions` prop. For manual rendering (e.g. in a detail view's bottom bar):\n\n```ts\nimport { renderActions } from '@neovici/cosmoz-queue/actions';\nimport { approveOrder } from './actions';\n\n// In a view component:\nconst bottomBar = renderActions({ items: [currentItem], open })([\n  approveOrder,\n]);\n```\n\n### Action types\n\n```ts\ninterface Action\u003cTItem, TDialog\u003e {\n  title: () =\u003e string;\n  applicable?: (item: TItem) =\u003e boolean;\n  button?: (opts: Action \u0026 ActionOpts) =\u003e unknown;\n  dialog: (opts: DialogOpts) =\u003e Dialogable | Promise\u003cDialogable\u003e;\n}\n\ninterface ActionOpts\u003cTItem\u003e {\n  items: TItem[];\n  open: (dialog: Dialogable) =\u003e void;\n  slot?: string;\n}\n```\n\n## Advanced: composing with hooks\n\nWhen `queue()` is too opinionated, use the individual hooks directly.\n\n### `useQueue()` + `renderQueue()`\n\n```ts\nimport { useQueue, renderQueue, renderNav } from '@neovici/cosmoz-queue';\n\nconst MyQueue = ({ heading }: { heading: string }) =\u003e {\n  const {\n    index, mobile, tabnav, items, setItems,\n    setSelected, setTotalAvailable, totalAvailable,\n    onItemClick, nav,\n  } = useQueue\u003cMyItem\u003e({\n    idHashParam: 'id',\n    tabHashParam: 'tab',\n  });\n\n  return renderQueue({\n    heading,\n    mobile,\n    index,\n    items,\n    tabnav,\n    totalAvailable,\n    nav,\n    list: html`\u003cmy-list\n      @visible-items-changed=${updateWith(setItems)}\n      @selected-items-changed=${updateWith(setSelected)}\n      @total-available-changed=${updateWith(setTotalAvailable)}\n      @omnitable-item-click=${onItemClick}\n    \u003e\u003c/my-list\u003e`,\n    renderItem: ({ item, nav }) =\u003e\n      html`\u003cmy-view .item=${item}\u003e${nav}\u003c/my-view\u003e`,\n    renderLoader: ({ item, nav }) =\u003e\n      html`\u003cmy-skeleton .item=${item}\u003e${nav}\u003c/my-skeleton\u003e`,\n  });\n};\n```\n\n### Individual hooks\n\n| Hook | Import | Purpose |\n|---|---|---|\n| `useTabs({ items, hashParam, mobile, fallback })` | `@neovici/cosmoz-queue` | Manages overview/split/queue tabs. Returns `{ tabnav, activeTab }`. |\n| `useDataNav(items, opts)` | `@neovici/cosmoz-queue` | Item navigation with prev/next, URL hash sync. Returns `{ item, setItem, next, prev, forward, index }`. |\n| `useSplit({ activeTab, ...splitOpts })` | `@neovici/cosmoz-queue` | Initializes Split.js when in split mode. |\n| `useListState()` | `@neovici/cosmoz-queue` | Creates `items`, `selected`, `totalAvailable` state with setters. |\n| `useListSSE({ entity, params, list$ })` | `@neovici/cosmoz-queue` | Subscribes to Server-Sent Events (`cosmoz-${entity}-updated`) for real-time list updates. |\n| `useFetchActions({ pathLocator, selected, api })` | `@neovici/cosmoz-queue` | Fetches available actions for selected items from an API. Returns `{ actions, actionRows, actionsFetching }`. |\n| `useAsyncAction(nav)` | `@neovici/cosmoz-queue` | Handles async action completion with automatic item removal from the list. Returns `{ listRef, onAsyncSimpleAction }`. |\n| `usePagination()` | `@neovici/cosmoz-queue` | URL hash-based page state. Returns `{ page, onPage }`. |\n\n## Utilities\n\n### Fetch helpers (`@neovici/cosmoz-queue/util/fetch`)\n\nPre-configured `fetch` wrapper with CORS and credentials:\n\n```ts\nimport {\n  fetch,\n  setBaseInit,\n  handleJSON,\n  RequestError,\n} from '@neovici/cosmoz-queue/util/fetch';\n\n// Configure default headers (call once at app startup)\nsetBaseInit({\n  headers: { 'X-Custom-Header': 'value' },\n  // Or use dynamic headers:\n  getHeaders: () =\u003e ({ Authorization: `Bearer ${getToken()}` }),\n});\n\n// fetch() includes mode: 'cors', credentials: 'include' by default\nconst response = await fetch('/api/orders');\nconst data = await handleJSON(response);\n```\n\n`RequestError` extends `Error` with `.response` and `.data` properties for structured error handling.\n\n### `itemClick()`\n\nMakes list cells clickable, dispatching `omnitable-item-click` events that the queue listens for:\n\n```ts\nimport { itemClick } from '@neovici/cosmoz-queue';\n\n// In an omnitable cell renderer:\nhtml`\u003ca @click=\"${itemClick({ index, activate: ['split', 'queue'] })}\"\u003e\n  ${item.title}\n\u003c/a\u003e`;\n```\n\nThe `activate` option specifies which tab to switch to. The queue picks the first non-disabled tab from the array.\n\n### Other utilities\n\n| Function | Description |\n|---|---|\n| `getItems(items, selected)` | Returns `selected` if non-empty, otherwise `items` |\n| `touch(list, id)` | Forces an omnitable item refresh by replacing it with a shallow copy |\n\n## Architecture\n\n```\nqueue() factory\n  |\n  +---\u003e useQueue()                     State orchestration\n  |       |\n  |       +---\u003e useListState()         items, selected, totalAvailable\n  |       +---\u003e useTabs()              overview | split | queue\n  |       +---\u003e useDataNav()           current item, prev/next, URL hash\n  |       +---\u003e useKeyNav()            arrow key navigation\n  |       +---\u003e useSplit()             Split.js initialization\n  |       +---\u003e useUpdates()           list-item-remove events\n  |\n  +---\u003e useAsyncAction()               Post-action item removal\n  |\n  +---\u003e renderQueue()                  Template composition\n          |\n          +---\u003e cosmoz-tabs-next       Tab bar (List / Split / Queue)\n          +---\u003e renderStats()          \"3-5 of 120\"\n          +---\u003e renderPagination()     Page prev/next\n          +---\u003e \u003cdiv.split\u003e\n                 +---\u003e list            cosmoz-omnitable (user-provided)\n                 +---\u003e cosmoz-slider   Animated detail view\n                        +---\u003e renderSlide() --\u003e renderView()\n                                                  |\n                                                  +---\u003e details()   Async fetch\n                                                  +---\u003e renderItem  or renderLoader\n```\n\n## Entry points\n\n| Import path | Description |\n|---|---|\n| `@neovici/cosmoz-queue` | Main: `queue()`, `useQueue()`, `renderQueue()`, navigation hooks, SSE, utilities |\n| `@neovici/cosmoz-queue/actions` | `action()`, `renderActions()`, `defaultButton()`, `actionCount()` |\n| `@neovici/cosmoz-queue/list` | `listCore()`, `column()`, `useListCore()`, `useListCoreState()`, `renderListCore()` |\n| `@neovici/cosmoz-queue/list/more` | `useMore()` -- progressive \"load more\" pagination |\n| `@neovici/cosmoz-queue/list/more/render` | `renderLoadMore()` -- \"Load more\" button |\n| `@neovici/cosmoz-queue/util/fetch` | `fetch()`, `setBaseInit()`, `handleJSON()`, `RequestError` |\n\n## Deprecation notices\n\n### `api` property → `details`\n\nThe `api` property on `useQueue()` / `queue()` is **deprecated** and will be\nremoved in v2.0.0. Use `details` instead:\n\n```ts\n// Before (deprecated)\nqueue({ api: (id, item) =\u003e apiUrl(`items/${id}`) })\n\n// After\nqueue({ details: (item) =\u003e fetch(apiUrl(`items/${item.id}`)).then(r =\u003e r.json()) })\n```\n\nSee [Migration guide](docs/migration-api-to-details.md) for more patterns.\n\n## License\n\n[Apache-2.0](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneovici%2Fcosmoz-queue","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fneovici%2Fcosmoz-queue","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneovici%2Fcosmoz-queue/lists"}