@codecks/fetch 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +317 -10
  2. package/dist/_exploration/api-requester.d.ts +4 -4
  3. package/dist/_exploration/api-requester.js +5 -4
  4. package/dist/_exploration/create-hooks.d.ts +5 -5
  5. package/dist/_exploration/create-hooks.js +193 -88
  6. package/dist/_exploration/loader.d.ts +2 -2
  7. package/dist/_exploration/loader.js +7 -2
  8. package/dist/_exploration/store.d.ts +4 -4
  9. package/dist/_exploration/store.js +84 -73
  10. package/dist/{_root-D1Faf8cg.d.ts → _root-rBEjvjCJ.d.ts} +1 -1
  11. package/dist/immediate-cache-DiH8tpJM.js +237 -0
  12. package/dist/{index-DUBsesYl.d.ts → index-obQtSRRg.d.ts} +1 -1
  13. package/dist/index.d.ts +4 -4
  14. package/dist/index.js +7 -6
  15. package/dist/{loader-types-Dp0CSVTf.d.ts → loader-types-Y0OHlOEK.d.ts} +4 -8
  16. package/dist/{loader-utils-Du-CTKH-.d.ts → loader-utils-rQFSDhwf.d.ts} +2 -2
  17. package/dist/{query-helpers-DsMMOtSO.js → query-helpers-DgC42MEH.js} +13 -9
  18. package/dist/{query-type-Dx5ZCv5N.d.ts → query-type-B0L3hwCo.d.ts} +2 -2
  19. package/dist/{store-B6JWZnAO.d.ts → store-LSWKTuHv.d.ts} +97 -6
  20. package/package.json +7 -6
  21. package/schema/models/account.md +104 -0
  22. package/schema/models/accountOnboarding.md +15 -0
  23. package/schema/models/accountOnboardingStep.md +14 -0
  24. package/schema/models/accountRole.md +19 -0
  25. package/schema/models/accountUserAchievement.md +17 -0
  26. package/schema/models/accountUserSetting.md +20 -0
  27. package/schema/models/activeTimeTracker.md +19 -0
  28. package/schema/models/activity.md +30 -0
  29. package/schema/models/affiliateCode.md +30 -0
  30. package/schema/models/affiliateCodeStat.md +19 -0
  31. package/schema/models/app.md +10 -0
  32. package/schema/models/appInstallation.md +19 -0
  33. package/schema/models/assigneeAssignment.md +18 -0
  34. package/schema/models/assigneeDeckAssignment.md +20 -0
  35. package/schema/models/attachment.md +23 -0
  36. package/schema/models/autoFinishedTimeTrackingSegment.md +20 -0
  37. package/schema/models/card.md +82 -0
  38. package/schema/models/cardDiffNotification.md +22 -0
  39. package/schema/models/cardHistory.md +20 -0
  40. package/schema/models/cardOrder.md +17 -0
  41. package/schema/models/cardOrderInDeck.md +18 -0
  42. package/schema/models/cardPreset.md +19 -0
  43. package/schema/models/cardSubscription.md +19 -0
  44. package/schema/models/cardUpvote.md +21 -0
  45. package/schema/models/cardsEffortHistory.md +9 -0
  46. package/schema/models/cardsFinishedHistory.md +16 -0
  47. package/schema/models/cardsStatusHistory.md +9 -0
  48. package/schema/models/cardsTimeToFinished.md +18 -0
  49. package/schema/models/dailyDiscordGuildVoteMembership.md +15 -0
  50. package/schema/models/dailyPublicProjectMembership.md +15 -0
  51. package/schema/models/deck.md +53 -0
  52. package/schema/models/deckAssignment.md +18 -0
  53. package/schema/models/deckGuardian.md +17 -0
  54. package/schema/models/deckSubscription.md +18 -0
  55. package/schema/models/discordGuild.md +30 -0
  56. package/schema/models/discordMember.md +21 -0
  57. package/schema/models/discordProjectNotification.md +19 -0
  58. package/schema/models/discordSlashCommand.md +28 -0
  59. package/schema/models/dueCard.md +18 -0
  60. package/schema/models/file.md +26 -0
  61. package/schema/models/handCard.md +18 -0
  62. package/schema/models/integration.md +23 -0
  63. package/schema/models/invoice.md +21 -0
  64. package/schema/models/lastSeenCardUpvote.md +17 -0
  65. package/schema/models/milestone.md +39 -0
  66. package/schema/models/milestoneProgress.md +14 -0
  67. package/schema/models/milestoneProject.md +17 -0
  68. package/schema/models/pinnedMilestone.md +22 -0
  69. package/schema/models/project.md +58 -0
  70. package/schema/models/projectOrder.md +18 -0
  71. package/schema/models/projectSelection.md +18 -0
  72. package/schema/models/projectTag.md +19 -0
  73. package/schema/models/projectUser.md +18 -0
  74. package/schema/models/projectUserSetting.md +15 -0
  75. package/schema/models/publicProjectInfo.md +20 -0
  76. package/schema/models/publicProjectMembership.md +17 -0
  77. package/schema/models/publicProjectVisit.md +16 -0
  78. package/schema/models/queueEntry.md +21 -0
  79. package/schema/models/queueSelection.md +19 -0
  80. package/schema/models/release.md +12 -0
  81. package/schema/models/resolvable.md +32 -0
  82. package/schema/models/resolvableEntry.md +28 -0
  83. package/schema/models/resolvableEntryHistory.md +20 -0
  84. package/schema/models/resolvableEntryReaction.md +23 -0
  85. package/schema/models/resolvableNotification.md +30 -0
  86. package/schema/models/resolvableParticipant.md +25 -0
  87. package/schema/models/resolvableParticipantHistory.md +23 -0
  88. package/schema/models/savedSearch.md +18 -0
  89. package/schema/models/sprint.md +42 -0
  90. package/schema/models/sprintConfig.md +38 -0
  91. package/schema/models/sprintConfigProgress.md +14 -0
  92. package/schema/models/sprintProgress.md +14 -0
  93. package/schema/models/sprintProject.md +17 -0
  94. package/schema/models/stripeAccountSync.md +29 -0
  95. package/schema/models/timeTrackingSegment.md +24 -0
  96. package/schema/models/timeTrackingSum.md +18 -0
  97. package/schema/models/user.md +67 -0
  98. package/schema/models/userDismissedHint.md +16 -0
  99. package/schema/models/userEmail.md +18 -0
  100. package/schema/models/userInvitation.md +20 -0
  101. package/schema/models/userInviteCode.md +23 -0
  102. package/schema/models/userOnboarding.md +14 -0
  103. package/schema/models/userProjectAccess.md +17 -0
  104. package/schema/models/userReportEmail.md +19 -0
  105. package/schema/models/userReportSetting.md +23 -0
  106. package/schema/models/userReportToken.md +18 -0
  107. package/schema/models/userTag.md +18 -0
  108. package/schema/models/visionBoard.md +21 -0
  109. package/schema/models/visionBoardQuery.md +22 -0
  110. package/schema/models/wizard.md +19 -0
  111. package/schema/models/workflowItem.md +45 -0
  112. package/schema/models/workflowItemHistory.md +18 -0
  113. package/schema/overview.md +110 -0
  114. package/schema/query-syntax.md +155 -0
package/README.md CHANGED
@@ -1,28 +1,335 @@
1
1
  # @codecks/fetch
2
2
 
3
- ## Usage
3
+ A type-safe query SDK for the [Codecks](https://www.codecks.io) API. Describe nested queries in a declarative DSL and get fully inferred TypeScript response types.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @codecks/fetch
9
+ ```
10
+
11
+ ## Getting started
4
12
 
5
13
  ```ts
6
14
  import {buildFetchersWithSimpleLoader} from "@codecks/fetch";
7
15
 
8
- const {fetchFromRoot, fetchInstance} = buildFetchersWithSimpleLoader({
9
- baseUrl: "https://api.codecks.io/",
10
- subdomain: "my-org",
16
+ const {fetchFromRoot, fetchInstance, fetchInstances, fetchFromInstance} =
17
+ buildFetchersWithSimpleLoader({
18
+ baseUrl: "https://api.codecks.io/",
19
+ subdomain: "my-org",
20
+ accessToken: "your-token",
21
+ });
22
+ ```
23
+
24
+ ### Configuration options
25
+
26
+ | Option | Type | Description |
27
+ | ------------- | ------------------------ | ------------------------------ |
28
+ | `baseUrl` | `string` | API base URL |
29
+ | `subdomain` | `string` | Sets the `X-Account` header |
30
+ | `accessToken` | `string` | Sets the `X-Auth-Token` header |
31
+ | `headers` | `Record<string, string>` | Additional request headers |
32
+ | `timeout` | `number` | Request timeout in ms |
33
+ | `fetch` | `typeof fetch` | Custom fetch implementation |
34
+
35
+ ## Fetching data
36
+
37
+ ### `fetchFromRoot` — query top-level relations
38
+
39
+ Use this to query entry points like `account`, `loggedInUser`, or `releases`.
40
+
41
+ ```ts
42
+ const result = await fetchFromRoot({
43
+ account: {
44
+ fields: ["name", "subdomain"],
45
+ },
46
+ });
47
+
48
+ console.log(result.account.name);
49
+ // ^ fully typed as string
50
+ ```
51
+
52
+ ### `fetchInstance` — fetch a single instance by model name and ID
53
+
54
+ ```ts
55
+ const card = await fetchInstance("card", "card-123", {
56
+ fields: ["title", "status"],
57
+ });
58
+
59
+ console.log(card.title);
60
+ ```
61
+
62
+ ### `fetchInstances` — fetch multiple instances
63
+
64
+ Returns a `Record<Id, Result>`.
65
+
66
+ ```ts
67
+ const cards = await fetchInstances("card", ["card-1", "card-2"], {
68
+ fields: ["title"],
69
+ });
70
+
71
+ console.log(cards["card-1"].title);
72
+ ```
73
+
74
+ ### `fetchFromInstance` — fetch from an existing instance reference
75
+
76
+ Any previously fetched instance can be passed to query more data from it.
77
+
78
+ ```ts
79
+ const account = result.account;
80
+ // account has { ~model: "account", ~key: "1" }
81
+
82
+ const details = await fetchFromInstance(account, {
83
+ fields: ["seats", "activeProjectCount"],
84
+ relations: {
85
+ roles: {fields: ["role"]},
86
+ },
11
87
  });
88
+ ```
12
89
 
13
- const rootResponse = await fetchFromRoot({
90
+ ## Querying relations
91
+
92
+ Relations are fetched by nesting them under `relations`. They can be nested to any depth.
93
+
94
+ ```ts
95
+ const result = await fetchFromRoot({
14
96
  account: {
15
97
  fields: ["name"],
98
+ relations: {
99
+ // belongsTo — returns a single object (or null if optional)
100
+ disabledBy: {fields: ["name"]},
101
+
102
+ // hasMany — returns an array
103
+ roles: {
104
+ fields: ["role"],
105
+ relations: {
106
+ user: {fields: ["name", "fullName"]},
107
+ },
108
+ },
109
+ },
16
110
  },
17
111
  });
18
112
 
19
- console.log(rootResponse);
20
- // > {account: {id: 1, name: "myOrg"}}
113
+ // result.account.disabledBy?.name
114
+ // result.account.roles[0].user.name
115
+ ```
116
+
117
+ ## hasMany variants
118
+
119
+ hasMany relations support several query modes. Non-default variants require an `as` alias.
120
+
121
+ ### Default (array)
122
+
123
+ ```ts
124
+ relations: {
125
+ roles: {
126
+ fields: ["role"],
127
+ orderBy: "-accountId",
128
+ limit: 10,
129
+ offset: 0,
130
+ },
131
+ }
132
+ // result.roles: Array<{role: string, ...}>
133
+ ```
134
+
135
+ ### Count
136
+
137
+ ```ts
138
+ relations: {
139
+ roles: {type: "count", as: "roleCount"},
140
+ }
141
+ // result.roleCount: number
142
+ ```
143
+
144
+ ### Exists
145
+
146
+ ```ts
147
+ relations: {
148
+ releases: {type: "exists", as: "hasReleases"},
149
+ }
150
+ // result.hasReleases: boolean
151
+ ```
152
+
153
+ ### First
154
+
155
+ Returns a single result or `null`. Requires `orderBy`.
156
+
157
+ ```ts
158
+ relations: {
159
+ roles: {
160
+ type: "first",
161
+ as: "firstRole",
162
+ orderBy: "-accountId",
163
+ fields: ["role"],
164
+ },
165
+ }
166
+ // result.firstRole: {role: string, ...} | null
167
+ ```
168
+
169
+ ### Multiple queries on the same relation
170
+
171
+ Pass an array of aliased queries to query the same relation in different ways:
21
172
 
22
- const card = await fetchInstance("card", 1, {
173
+ ```ts
174
+ relations: {
175
+ roles: [
176
+ {as: "adminRoles", fields: ["role"], filter: {role: "admin"}},
177
+ {as: "roleCount", type: "count"},
178
+ ],
179
+ }
180
+ // result.adminRoles: Array<...>
181
+ // result.roleCount: number
182
+ ```
183
+
184
+ ## Filtering
185
+
186
+ Filters are available on all hasMany variants.
187
+
188
+ ### Simple equality
189
+
190
+ ```ts
191
+ filter: {
192
+ status: "done";
193
+ }
194
+ // shorthand for {status: {op: "eq", value: "open"}}
195
+ ```
196
+
197
+ ### Null checks
198
+
199
+ ```ts
200
+ filter: {
201
+ assigneeId: null;
202
+ }
203
+ ```
204
+
205
+ ### Comparison operators
206
+
207
+ ```ts
208
+ filter: {
209
+ createdAt: {op: "gt", value: "2025-01-01"},
210
+ effort: {op: "lte", value: 5},
211
+ }
212
+ ```
213
+
214
+ Available operators: `eq`, `neq`, `lt`, `lte`, `gt`, `gte`.
215
+
216
+ ### Set operators
217
+
218
+ ```ts
219
+ filter: {
220
+ derivedStatus: {op: "in", value: ["review", "blocked"]},
221
+ }
222
+ ```
223
+
224
+ Also available: `notIn`.
225
+
226
+ ### String operators
227
+
228
+ ```ts
229
+ filter: {
230
+ title: {op: "contains", value: "bug"},
231
+ }
232
+ ```
233
+
234
+ ### Array operators
235
+
236
+ ```ts
237
+ filter: {
238
+ tags: {op: "has", value: "urgent"},
239
+ masterTags: {op: "overlaps", value: ["frontend", "backend"]},
240
+ }
241
+ ```
242
+
243
+ ### Logical combinators
244
+
245
+ ```ts
246
+ filter: {
247
+ $or: [
248
+ {status: "open"},
249
+ {status: "started"},
250
+ ],
251
+ }
252
+ ```
253
+
254
+ Also available: `$and`.
255
+
256
+ ### Relation filters and negation
257
+
258
+ ```ts
259
+ filter: {
260
+ // cards that have an assignee named "Alice"
261
+ assignee: {name: "Alice"},
262
+ // cards that do NOT belong to deck "Backlog"
263
+ "!deck": {title: "Backlog"},
264
+ }
265
+ ```
266
+
267
+ ## Ordering
268
+
269
+ ```ts
270
+ // ascending
271
+ orderBy: "createdAt"
272
+
273
+ // descending (prefix with -)
274
+ orderBy: "-createdAt"
275
+
276
+ // multiple
277
+ orderBy: ["status", "-createdAt"]
278
+
279
+ // object syntax
280
+ orderBy: {field: "createdAt", dir: "desc"}
281
+ ```
282
+
283
+ ## Response shape
284
+
285
+ Every returned instance includes:
286
+
287
+ - **Requested fields** — only the fields you asked for
288
+ - **Key fields** — always included (e.g. `cardId` for cards, `id` for accounts)
289
+ - **`~model`** — the model name (e.g. `"card"`)
290
+ - **`~key`** — the instance's unique key
291
+
292
+ ```ts
293
+ const card = await fetchInstance("card", "card-123", {
23
294
  fields: ["title"],
24
295
  });
296
+ // {
297
+ // cardId: "card-123",
298
+ // title: "My Card",
299
+ // "~model": "card",
300
+ // "~key": "card-123",
301
+ // }
302
+ ```
303
+
304
+ All response types are fully inferred from your query — TypeScript knows exactly which fields and relations are present.
305
+
306
+ ## Schema reference for LLMs
307
+
308
+ This package ships with generated markdown files describing every API model, its fields, and relations. These are designed for LLM-based tools (Claude Code, Cursor, Copilot, etc.) that need to discover the API schema without relying on TypeScript autocomplete.
309
+
310
+ ```
311
+ node_modules/@codecks/fetch/schema/
312
+ overview.md # Root entry points + index of all models
313
+ query-syntax.md # Query DSL reference with examples
314
+ models/
315
+ card.md # Fields + relations for the card model
316
+ account.md # Fields + relations for the account model
317
+ ... # One file per model
318
+ ```
319
+
320
+ Point your LLM's project instructions (e.g. `CLAUDE.md`) at `schema/overview.md` as a starting point, then let it drill into individual model files as needed.
321
+
322
+ ## Custom loader
323
+
324
+ For advanced use cases (batching, caching, custom transports), you can provide your own `DataLoader`:
25
325
 
26
- console.log(card);
27
- // > {cardId: 1, title: "My Title"}
326
+ ```ts
327
+ import {buildFetchers} from "@codecks/fetch";
328
+
329
+ const {fetchFromRoot} = buildFetchers({
330
+ fetchModel: async (model, ids, query) => {
331
+ // your custom loading logic
332
+ return recordOfResults;
333
+ },
334
+ });
28
335
  ```
@@ -1,7 +1,7 @@
1
- import "../query-type-Dx5ZCv5N.js";
2
- import "../index-DUBsesYl.js";
3
- import { FetchOptions } from "../loader-utils-Du-CTKH-.js";
4
- import { BaseRequester, MissingDataRequest } from "../loader-types-Dp0CSVTf.js";
1
+ import "../query-type-B0L3hwCo.js";
2
+ import "../index-obQtSRRg.js";
3
+ import { FetchOptions } from "../loader-utils-rQFSDhwf.js";
4
+ import { BaseRequester, MissingDataRequest } from "../loader-types-Y0OHlOEK.js";
5
5
 
6
6
  //#region src/_exploration/api-requester.d.ts
7
7
  declare class ApiRequester implements BaseRequester {
@@ -1,5 +1,5 @@
1
1
  import { configuredFetch, ensureMapValue } from "../collection-utils-CYJaA1a9.js";
2
- import { serializeModel } from "../query-helpers-DsMMOtSO.js";
2
+ import { serializeModel } from "../query-helpers-DgC42MEH.js";
3
3
 
4
4
  //#region src/_exploration/utils/concurrency-limiter.ts
5
5
  var ConcurrencyLimiter = class {
@@ -82,10 +82,11 @@ var ApiRequester = class {
82
82
  relations: new Map()
83
83
  }));
84
84
  if (req.type === "field") instanceData.fields.add(req.field);
85
- else if (req.type === "relation") {
85
+ else if (req.type === "relation") if (req.contents.asField) instanceData.fields.add(req.relKey);
86
+ else {
86
87
  const existing = instanceData.relations.get(req.relKey);
87
- if (existing && !existing.asField && !req.query.asField) instanceData.relations.set(req.relKey, mergeRelations(existing, req.query));
88
- else instanceData.relations.set(req.relKey, req.query);
88
+ if (existing) instanceData.relations.set(req.relKey, mergeRelations(existing, req.contents));
89
+ else instanceData.relations.set(req.relKey, req.contents);
89
90
  }
90
91
  }
91
92
  const query = {};
@@ -1,8 +1,8 @@
1
- import { InferModelQuery, InferRelQuery, ModelDesc, ModelQuery, RelQuery, RelationEntry, TypedField } from "../query-type-Dx5ZCv5N.js";
2
- import { AccountId, AccountOnboardingStepId, ActiveTimeTrackerId, ActivityId, AffiliateCodeId, AppId, AppInstallationId, AttachmentId, CardId, CardPresetId, CardSubscriptionId, CardUpvoteId, DeckId, DeckSubscriptionId, DiscordGuildId, DiscordMemberId, DiscordProjectNotificationId, DiscordSlashCommandId, FileId, IntegrationId, InvoiceId, MilestoneId, PinnedMilestoneId, ProjectId, ProjectSelectionId, ProjectTagId, QueueEntryId, QueueSelectionId, ReleaseId, ResolvableEntryId, ResolvableEntryReactionId, ResolvableId, SavedSearchId, SprintConfigId, SprintId, TimeTrackingSegmentId, UserEmailId, UserId, UserInvitationId, UserInviteCodeId, UserReportEmailId, UserReportSettingId, UserReportTokenId, UserTagId, VisionBoardId, VisionBoardQueryId, WizardId, WorkflowItemId } from "../index-DUBsesYl.js";
3
- import "../loader-types-Dp0CSVTf.js";
4
- import { _rootDesc$1 as _rootDesc } from "../_root-D1Faf8cg.js";
5
- import { Store } from "../store-B6JWZnAO.js";
1
+ import { InferModelQuery, InferRelQuery, ModelDesc, ModelQuery, RelQuery, RelationEntry, TypedField } from "../query-type-B0L3hwCo.js";
2
+ import { AccountId, AccountOnboardingStepId, ActiveTimeTrackerId, ActivityId, AffiliateCodeId, AppId, AppInstallationId, AttachmentId, CardId, CardPresetId, CardSubscriptionId, CardUpvoteId, DeckId, DeckSubscriptionId, DiscordGuildId, DiscordMemberId, DiscordProjectNotificationId, DiscordSlashCommandId, FileId, IntegrationId, InvoiceId, MilestoneId, PinnedMilestoneId, ProjectId, ProjectSelectionId, ProjectTagId, QueueEntryId, QueueSelectionId, ReleaseId, ResolvableEntryId, ResolvableEntryReactionId, ResolvableId, SavedSearchId, SprintConfigId, SprintId, TimeTrackingSegmentId, UserEmailId, UserId, UserInvitationId, UserInviteCodeId, UserReportEmailId, UserReportSettingId, UserReportTokenId, UserTagId, VisionBoardId, VisionBoardQueryId, WizardId, WorkflowItemId } from "../index-obQtSRRg.js";
3
+ import "../loader-types-Y0OHlOEK.js";
4
+ import { _rootDesc$1 as _rootDesc } from "../_root-rBEjvjCJ.js";
5
+ import { Store } from "../store-LSWKTuHv.js";
6
6
 
7
7
  //#region src/_exploration/create-hooks.d.ts
8
8
  declare const createHooks: (store: Store) => {