@versionone/agility-sdk 1.0.0-alpha.2 → 1.0.0-alpha.4

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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,135 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0-alpha.4 — what the three adoptions asked for
4
+
5
+ ### Added
6
+
7
+ - **Two narrow rest-1 paths** (agility-rooms): `client.data.operate(oid,
8
+ op)` runs an operation for its result — api/asset performs a Copy but
9
+ reports only the source; rest-1 answers the copy's OID — and
10
+ `client.copy(oid)` is that for Copy. `client.data.template(type, {
11
+ ctx })` reads `New/<Type>?ctx=`: what the type's initializer fills in
12
+ for a context (a Timebox's next name and dates, schedule, state and
13
+ owner; a Story's carry-over from a Request), translated like a row,
14
+ without an `_oid`. Neither sends attribute values: rest-1 still carries
15
+ no update path.
16
+ - **`client.fields`**: `describe(type, scope)` joins what a form needs —
17
+ the attributes the server requires on a create (meta), the ones the
18
+ product shows for the type in that scope (`api/visibleFields`, custom
19
+ fields included once configured), and the scope's scheme's important
20
+ fields with whether they are enforced at close; `visible(types,
21
+ scopes)` and `visibleByAsset(oid)` are the endpoints alone. New wire
22
+ module `fields`.
23
+ - **`client.urls.asset` takes an asset Number** as well as an OID
24
+ (`assetdetail.v1?Number=S-01001`): the readable form a CLI prints and a
25
+ chat message shows (gh-v1, slack bot).
26
+ - **`Outcome.requestId`**: the server's id for a batch, worth logging
27
+ (slack bot). Null for an outcome merged from several sends.
28
+ - `outcomeOf` tolerating a partial envelope is now a stated contract
29
+ with a test: consumers' fakes may send only the fields they need.
30
+
31
+ ### Fixed
32
+
33
+ - `parseViolation`: `tag` is null for the token spelling
34
+ (`Violation'Invalid'Epic.Scope`); it was the token, which is not a loc-2
35
+ tag (gh-v1).
36
+ - `CHANGELOG.md` ships in the npm package.
37
+ - Doc notes: `kind: "usage"` means nothing was sent (status 0), so an
38
+ error formatter should not blame the server; `client.query`'s resend
39
+ after a failure needs `retries` above its default of 0; the deprecation
40
+ notes on `queryV1` and `assets.query` say where each endpoint is.
41
+
42
+ ### Migration note (from alpha.3's read path)
43
+
44
+ - A rejected query through `client.query` is an `ApiError` with kind
45
+ `invalid` and code `QUERY_REJECTED`; `message` holds the server's
46
+ sentences and `details` its whole error document. A formatter that
47
+ printed `details` for api/asset's `CommandFailureError` now prints a JSON
48
+ document; print `message`.
49
+
50
+ Alpha releases may break between versions. Every breaking change is listed
51
+ here with the one-line migration a consumer applies. A renamed export keeps
52
+ its old name as a `@deprecated` alias for one alpha and is removed in the
53
+ next; `1.0.0` follows agility-rooms' adoption, and semver after that.
54
+
55
+ ## 1.0.0-alpha.3 — the breaking batch (plan 005, step 2)
56
+
57
+ ### Breaking
58
+
59
+ - **`client.assets.send` answers an `Outcome`, not the api/asset envelope.**
60
+ `created`, `modified`, `operated` (momentless OIDs), `failures` (by index,
61
+ each with its parsed `violation`), `queryResults` (one slot per query
62
+ command, `null` where it failed), `durationSeconds`. Migration:
63
+ `res.assetsCreated.oidTokens` → `res.created`;
64
+ `res.commandFailures.count` → `res.failures.length`;
65
+ `res.queryResult.results[i]` → `res.queryResults[i]`. The wire module
66
+ `assetApi.send` still returns the envelope for a passthrough.
67
+ `CommandFailureError` gains `outcome` beside `response`.
68
+ - **Every SDK error is an `ApiError` with a `kind`** (`auth`, `forbidden`,
69
+ `notFound`, `invalid`, `transient`, `aborted`, `commandFailure`,
70
+ `protocol`, `usage`, `unknown`). Mistakes caught before any request
71
+ (an unpaged read, an empty batch, a bad OID token or asset number) throw
72
+ `UsageError`, not `Error`/`TypeError`. `UnguardableError` is a
73
+ `UsageError`; `GuardedPassError`, `BatchSendError`, `BatchRecordError`
74
+ and `AttachmentUploadError` are `WrappedError`s carrying the cause's
75
+ kind. Migration: a `catch` on `TypeError` becomes `instanceof
76
+ UsageError`; a branch on `status === 401` can become `kind === "auth"`.
77
+ - **rest-1 reads take a query's spelling**: `client.data.list(type, {
78
+ select, filter, … })`. `sel` and `where` still work this alpha
79
+ (deprecated) and go in the next.
80
+
81
+ ### Deprecated (removed in the next alpha)
82
+
83
+ - `client.queryV1.query` / `.queries` → `client.query` / `client.queries`.
84
+ - `client.assets.query` / `.queries` → `client.query` / `client.queries`
85
+ (or `client.queryPage` for a total).
86
+ - `client.attachments.url`, `client.embeddedImages.url` →
87
+ `client.urls.attachment`, `client.urls.embeddedImage`.
88
+ - rest-1 `sel` / `where` → `select` / `filter`.
89
+
90
+ ### Added
91
+
92
+ - The generated model is refreshed from meta.v1 26.3.1.13 (it was a
93
+ 0.0.0.0 dev build): `Workflow`, `TeamWorkflow` and `WorkflowTransition`
94
+ arrive, with `Status.AllowedStatusTransitions`,
95
+ `Status.WorkflowStatusTransitions`, `Role.AllowedStatusTransitions` and
96
+ `Team.TeamWorkflows`. Nothing was removed or changed.
97
+ `scripts/download-metamodel.mjs` now strips an instance's own
98
+ definitions and reports them, so a refresh is reviewable.
99
+ - Reads on the client: `client.query`, `queries` (query.v1: read-only,
100
+ retried like a GET), `client.get(oid, select)`, `client.queryPage(q)`
101
+ (total and `more`, via api/asset), `client.pages(q, { limit })` and
102
+ `client.all(q, { limit })` (page by page up to a limit, default 1000),
103
+ `client.me()` (the member the credentials belong to).
104
+ - Typed queries: a literal `from` completes `select` from the type's
105
+ attributes, and rows are typed by what was selected (`row.Name` a
106
+ string, `row.Nmae` a compile error, `Status.Name` unknown, an aggregate
107
+ a number). An OID as `from` types by its type. `from` still takes an
108
+ alias or an unknown type; variables and `query<Story>` work as before.
109
+ - Custom list types (`Custom_<Name>`, the one custom asset type Agility
110
+ allows, a subtype of List) type as `CustomList`: List's attributes and
111
+ the type's own token as `AssetType`, as a `from` or as an OID. Custom
112
+ attributes (`Custom_…`) and the reciprocal side of a custom relation
113
+ (`ReciprocalOf_…`, which the server adds on the related type) are
114
+ accepted on every type as attribute values.
115
+ - `client.urls.asset(oid)`: the asset's page (`assetdetail.v1?oid=`).
116
+ - `parseViolation(message)` and `CommandFailureDetail.violation`: the
117
+ rule, attribute token and loc-2 tag of a validation failure.
118
+ - `kindOf(status, code)`, `ErrorKind`, `UsageError`, `WrappedError`.
119
+
120
+ ## 1.0.0-alpha.2
121
+
122
+ - Releases are staged with `npm stage publish` and approved by a
123
+ maintainer with 2FA.
124
+
125
+ ## 1.0.0-alpha.1
126
+
127
+ - Batch reverse uses the type's operations and the close cascade; the
128
+ guard sends a second command on one asset as a second pass; Moment is
129
+ never a guard stamp.
130
+
131
+ ## 1.0.0-alpha.0
132
+
133
+ - First publish: `AgilityApiClient`, wire modules for api/asset,
134
+ query.v1, rest-1 reads, meta.v1, loc-2.v1 and attachments; services for
135
+ schema, resolve, guard, batch, attachments and embedded images.
package/README.md CHANGED
@@ -16,7 +16,8 @@ npm install @versionone/agility-sdk@alpha
16
16
  import { AgilityApiClient } from "@versionone/agility-sdk";
17
17
 
18
18
  const client = new AgilityApiClient({ baseUrl: "https://www14.v1host.com/v1sdktesting", accessToken: process.env.V1_ACCESS_TOKEN });
19
- const stories = await client.queryV1.query({ from: "Story", select: ["Name", "Estimate"], page: { size: 10, start: 0 } });
19
+ const stories = await client.query({ from: "Story", select: ["Name", "Estimate"], page: { size: 10, start: 0 } });
20
+ // stories[0].Estimate is a number | null; stories[0].Nmae does not compile
20
21
  ```
21
22
 
22
23
  ## Working on the SDK
@@ -34,20 +35,28 @@ npm test
34
35
  - `npm run typecheck` - run TypeScript type checks
35
36
  - `npm run verify` - run typecheck + build + tests
36
37
  - `npm run test` - run unit tests
37
- - `npm run download:metamodel -- <baseAgilityUrl>` - download meta model for a given Agility instance
38
+ - `npm run download:metamodel -- <baseAgilityUrl>` - refresh `metamodel.json` from an instance on the current release (instance-specific definitions stripped; `--raw` keeps them), then `npm run generate:types`. The cleanest source is a dev instance on a blank database (Core's gulp task for that); a hosted instance on the release gives the same file. The generated model is from meta.v1 **26.3.1.13** (hosted, 2026-11-12)
38
39
 
39
40
  ## Public API
40
41
 
41
- - `AgilityApiClient` - top-level SDK client (`meta`, `data` for rest-1, `assets` for api/asset, `localize` for loc-2, `schema`), and the services on it:
42
- - `queryV1.query` / `queryV1.queries` - reads through query.v1: the same query bodies as `assets.query`, rows translated the same way, but the endpoint is read-only and answers with rows alone, so it is the safe place to explore a query
42
+ Changes between alphas are in `CHANGELOG.md`, each with its migration line.
43
+
44
+ - `AgilityApiClient` - the client. One query body everywhere; the client picks the endpoint:
45
+ - `query` / `queries` - rows, translated (numbers as numbers, booleans as booleans, a single relation as one `Ref`), through query.v1: read-only, so a request is retried like a GET. A page is required; a refused query throws, never an empty result. Written with a literal `from`, `select` completes from the type's attributes and the rows are typed by what was selected
46
+ - `get(oid, select)` - one asset or null; `queryPage(q)` - rows with the total and whether more follow; `pages(q, { limit })` / `all(q, { limit })` - page by page up to a limit (default 1000); `me()` - the member the credentials belong to
47
+ - `assets.send` - an api/asset batch, answered as an `Outcome` (`created`, `modified`, `operated`, `failures` with parsed violations, `queryResults`); `throwOnFailure` throws a `CommandFailureError` carrying the same Outcome. `assets.preview` - the projection. `assets.sendGuarded` - a batch as a compare-and-set on each asset's ChangeDate; changed assets come back `skipped`
48
+ - `batch.run` / `batch.reversible` - preview, snapshot, (confirm,) send guarded, and a receipt with a reverse batch; undo reactivates a closed asset before deleting it and reopens what a close cascaded to
43
49
  - `resolve.numbers` / `resolve.ref` - asset numbers and OID tokens to assets, in one request
44
- - `assets.sendGuarded` - a batch as a compare-and-set on each asset's ChangeDate; changed assets come back `skipped`
45
- - `attachments.list` / `page` / `info` / `get` / `add` / `put` / `remove` / `url` - files attached to an asset: metadata on the Attachment asset, bytes through attachment.img; `page` adds the total and whether more pages follow; `get`'s `contentType` is the type the server sent the bytes with; `add` sends a small file inline (Base64, one request) and a large one in two steps (create, then upload), and a failed upload throws `AttachmentUploadError` naming the empty attachment it left
46
- - `embeddedImages.upload` / `url` / `normalize` - images in rich text: upload returns the `<img src>` URL; `normalize` rewrites `embedded.img` references to this instance's URL. They cannot be deleted
47
- - `batch.run` / `batch.reversible` - preview, snapshot, (confirm,) send guarded, and a receipt with a reverse batch. Undo (`batch.reversible`) reactivates a closed asset before deleting it, and a second command on one asset is sent as a second guarded pass. The reverse uses only operations the type has in meta (a created Conversation, which has no `Delete`, is a note), and undoing an `Inactivate` or `QuickClose` reopens the descendants that close cascaded to (`receipt.openDescendants`, `receipt.cascaded`), parent first. A command the server reports as failed lands in `receipt.failed`, not `skipped`. `confirm` sees the touched assets' names; aliased updates/executes are sent as written with `guard: false`; a failure after the send throws `BatchRecordError` carrying the receipt
48
- - `TypeCache` - one instance's loaded type definitions, shared by many clients (`new AgilityApiClient(config, { types })`): a server that builds a client per user fetches each type once
50
+ - `attachments.list` / `page` / `info` / `get` / `add` / `put` / `remove` - files attached to an asset; `add` sends a small file inline and a large one in two steps, and a failed upload throws `AttachmentUploadError` naming the empty attachment it left
51
+ - `embeddedImages.upload` / `normalize` - images in rich text; they cannot be deleted
52
+ - `copy(oid)` - the Copy operation, answering the copy's OID (api/asset performs it but never says what it made); `data.operate(oid, op)` is the general form
53
+ - `fields.describe(type, scope)` - what a form needs: required attributes (meta), the ones the product shows there (`api/visibleFields`, custom fields included), the scheme's important fields and whether they are enforced at close
54
+ - `urls.asset(oidOrNumber)` / `attachment` / `embeddedImage` - where a browser opens each
55
+ - `data.list` / `get` / `history` / `template` - rest-1 reads: deleted assets, history, and a new asset's template for a context (`New/<Type>?ctx=`); `meta.*` - meta.v1 raw; `schema` - the translated, cached view; `localize` - loc-2 labels for tags (a violation's `tag` is one)
56
+ - Errors: everything thrown is an `ApiError` with a `kind` (`auth`, `forbidden`, `notFound`, `invalid`, `transient`, `aborted`, `commandFailure`, `protocol`, `usage`, `unknown`). A mistake caught before any request is a `UsageError`
57
+ - `TypeCache` - one instance's loaded type definitions, shared by many clients (`new AgilityApiClient(config, { types })`)
49
58
  - `createFetchTransport` - configurable HTTP transport: `credentials` for same-origin cookie auth, `onUnauthorized` to re-authenticate on a 401 (resends idempotent requests once), and a per-request `signal`; `client.withSignal(signal)` ties a whole client to one
50
- - `ApiError` - typed API error; `CommandFailureError` - an api/asset batch in which commands failed (`failures` by index, plus the `response`)
59
+ - The wire modules (`assetApi`, `queryV1`, `rest1`, `meta`, `loc2`, `attachment`) are exported for passthroughs and transport authors; they speak the endpoints' own shapes
51
60
 
52
61
  API reference: https://docs.digital.ai/agility-api/
53
62
 
@@ -68,7 +77,11 @@ No registry package yet. Pin a commit: `npm install github:versionone/VersionOne
68
77
  Versions are published to npmjs by `.github/workflows/publish.yml` when a
69
78
  tag `v<version>` is pushed; the tag must equal `package.json`'s version. A
70
79
  prerelease (`1.0.0-alpha.0`, `-beta.N`, `-rc.N`) goes out on its own
71
- dist-tag (`alpha`, `beta`, `rc`), so `latest` is never a prerelease.
80
+ dist-tag (`alpha`, `beta`, `rc`). Until `1.0.0`, a maintainer moves
81
+ `latest` to the newest alpha by hand (`npm dist-tag add
82
+ @versionone/agility-sdk@<version> latest`), so a bare `npm install` gets
83
+ the current API rather than the first publish; from `1.0.0` on, `latest`
84
+ is never a prerelease.
72
85
 
73
86
  ```bash
74
87
  npm version 1.0.0-alpha.2 --no-git-tag-version # bump, commit, merge