@volter/twin-standard 1.0.96 → 1.0.97
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/client-sdks/bun.lock +7 -0
- package/client-sdks/package.json +2 -1
- package/dist/client-sdks/bun.lock +7 -0
- package/dist/client-sdks/package.json +2 -1
- package/dist/src/clients/linear.d.ts +2 -0
- package/dist/src/clients/linear.js +48 -0
- package/package.json +2 -2
- package/src/clients/linear.ts +41 -0
package/client-sdks/bun.lock
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
"": {
|
|
6
6
|
"name": "twin-standard-client-sdks",
|
|
7
7
|
"dependencies": {
|
|
8
|
+
"@linear/sdk": "86.0.0",
|
|
8
9
|
"@sentry/browser": "10.38.0",
|
|
9
10
|
"@sentry/core": "10.38.0",
|
|
10
11
|
"@sentry/node": "10.38.0",
|
|
@@ -23,6 +24,10 @@
|
|
|
23
24
|
|
|
24
25
|
"@apm-js-collab/tracing-hooks": ["@apm-js-collab/tracing-hooks@0.3.1", "", { "dependencies": { "@apm-js-collab/code-transformer": "^0.8.0", "debug": "^4.4.1", "module-details-from-path": "^1.0.4" } }, "sha512-Vu1CbmPURlN5fTboVuKMoJjbO5qcq9fA5YXpskx3dXe/zTBvjODFoerw+69rVBlRLrJpwPqSDqEuJDEKIrTldw=="],
|
|
25
26
|
|
|
27
|
+
"@graphql-typed-document-node/core": ["@graphql-typed-document-node/core@3.2.0", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="],
|
|
28
|
+
|
|
29
|
+
"@linear/sdk": ["@linear/sdk@86.0.0", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.2.0" } }, "sha512-Lr47874CGGPCDiPQoKYjcZDWyyu18N8zzEGvss86uzjZaeZrhNsuwKTARswG27VAJ3fPVJAvl/5dPl8QwndSlg=="],
|
|
30
|
+
|
|
26
31
|
"@opentelemetry/api": ["@opentelemetry/api@1.9.1", "", {}, "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q=="],
|
|
27
32
|
|
|
28
33
|
"@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.211.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg=="],
|
|
@@ -165,6 +170,8 @@
|
|
|
165
170
|
|
|
166
171
|
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
|
167
172
|
|
|
173
|
+
"graphql": ["graphql@17.0.2", "", {}, "sha512-FRWbddMxfkjiB7z+aQDWIR+E34xo9I8c9mtK2RPv8PmMzKRvrdsreHL/Ui/TmwHJfhHChEtsFPyMHKI+xuarQQ=="],
|
|
174
|
+
|
|
168
175
|
"hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="],
|
|
169
176
|
|
|
170
177
|
"import-in-the-middle": ["import-in-the-middle@2.0.6", "", { "dependencies": { "acorn": "^8.15.0", "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^2.2.0", "module-details-from-path": "^1.0.4" } }, "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw=="],
|
package/client-sdks/package.json
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
"": {
|
|
6
6
|
"name": "twin-standard-client-sdks",
|
|
7
7
|
"dependencies": {
|
|
8
|
+
"@linear/sdk": "86.0.0",
|
|
8
9
|
"@sentry/browser": "10.38.0",
|
|
9
10
|
"@sentry/core": "10.38.0",
|
|
10
11
|
"@sentry/node": "10.38.0",
|
|
@@ -23,6 +24,10 @@
|
|
|
23
24
|
|
|
24
25
|
"@apm-js-collab/tracing-hooks": ["@apm-js-collab/tracing-hooks@0.3.1", "", { "dependencies": { "@apm-js-collab/code-transformer": "^0.8.0", "debug": "^4.4.1", "module-details-from-path": "^1.0.4" } }, "sha512-Vu1CbmPURlN5fTboVuKMoJjbO5qcq9fA5YXpskx3dXe/zTBvjODFoerw+69rVBlRLrJpwPqSDqEuJDEKIrTldw=="],
|
|
25
26
|
|
|
27
|
+
"@graphql-typed-document-node/core": ["@graphql-typed-document-node/core@3.2.0", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="],
|
|
28
|
+
|
|
29
|
+
"@linear/sdk": ["@linear/sdk@86.0.0", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.2.0" } }, "sha512-Lr47874CGGPCDiPQoKYjcZDWyyu18N8zzEGvss86uzjZaeZrhNsuwKTARswG27VAJ3fPVJAvl/5dPl8QwndSlg=="],
|
|
30
|
+
|
|
26
31
|
"@opentelemetry/api": ["@opentelemetry/api@1.9.1", "", {}, "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q=="],
|
|
27
32
|
|
|
28
33
|
"@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.211.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg=="],
|
|
@@ -165,6 +170,8 @@
|
|
|
165
170
|
|
|
166
171
|
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
|
167
172
|
|
|
173
|
+
"graphql": ["graphql@17.0.2", "", {}, "sha512-FRWbddMxfkjiB7z+aQDWIR+E34xo9I8c9mtK2RPv8PmMzKRvrdsreHL/Ui/TmwHJfhHChEtsFPyMHKI+xuarQQ=="],
|
|
174
|
+
|
|
168
175
|
"hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="],
|
|
169
176
|
|
|
170
177
|
"import-in-the-middle": ["import-in-the-middle@2.0.6", "", { "dependencies": { "acorn": "^8.15.0", "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^2.2.0", "module-details-from-path": "^1.0.4" } }, "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw=="],
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// The dependency demand's official Linear SDK pin, unchanged, over the World's GraphQL routing.
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { standardPackageFile } from "../package-file.js";
|
|
4
|
+
const sdk = createRequire(new URL('package.json', standardPackageFile('client-sdks/')));
|
|
5
|
+
async function setup(c) {
|
|
6
|
+
const door = async (path, body) => {
|
|
7
|
+
const answer = await c.fetch(`https://${c.host}${path}`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) });
|
|
8
|
+
if (!answer.ok)
|
|
9
|
+
throw new Error(`${path} answered ${answer.status}: ${await answer.text()}`);
|
|
10
|
+
return await answer.json();
|
|
11
|
+
};
|
|
12
|
+
const made = await door('/_twin/workspaces', { name: 'SDK workspace', urlKey: 'sdk-workspace', owner: { name: 'SDK owner', email: 'owner@linear-sdk.test', password: 'synthetic-sdk-password' } });
|
|
13
|
+
const key = await door('/_twin/api-keys', { email: 'owner@linear-sdk.test', label: 'SDK case' });
|
|
14
|
+
return { owner: made.owner, apiKey: key.key, client: new (sdk('@linear/sdk').LinearClient)({ apiKey: key.key }) };
|
|
15
|
+
}
|
|
16
|
+
export const cases = [{
|
|
17
|
+
id: 'issue-create-update-and-read',
|
|
18
|
+
asks: 'the SDK reads its full viewer/team/issue models, writes and reads an issue, and observes native counts after archive',
|
|
19
|
+
source: 'https://linear.app/developers/sdk-fetching-and-modifying-data "To create a model, call the Linear Client mutation and pass an input object:"',
|
|
20
|
+
async run(c) {
|
|
21
|
+
const { owner, apiKey, client } = await setup(c);
|
|
22
|
+
const me = await client.viewer;
|
|
23
|
+
if (me.id !== owner || me.email !== 'owner@linear-sdk.test')
|
|
24
|
+
return ['viewer does not identify the key owner'];
|
|
25
|
+
const madeTeam = await client.createTeam({ name: 'SDK team', key: 'SDK' });
|
|
26
|
+
const team = await madeTeam.team;
|
|
27
|
+
if (!madeTeam.success || !team?.id)
|
|
28
|
+
return ['createTeam returned no successful team'];
|
|
29
|
+
const madeIssue = await client.createIssue({ teamId: team.id, title: 'SDK issue', description: 'Synthetic issue body' });
|
|
30
|
+
const issue = await madeIssue.issue;
|
|
31
|
+
if (!madeIssue.success || !issue?.id || issue.title !== 'SDK issue')
|
|
32
|
+
return ['createIssue returned no successful issue'];
|
|
33
|
+
const updated = await client.updateIssue(issue.id, { title: 'SDK issue updated', priority: 2 });
|
|
34
|
+
if (!updated.success)
|
|
35
|
+
return ['updateIssue did not succeed'];
|
|
36
|
+
const got = await client.issue(issue.id);
|
|
37
|
+
if (got.id !== issue.id || got.title !== 'SDK issue updated' || got.description !== 'Synthetic issue body' || got.priority !== 2)
|
|
38
|
+
return ['issue did not preserve its id, changed title/priority and original description'];
|
|
39
|
+
if ((await client.viewer).createdIssueCount !== 1 || (await client.team(team.id)).issueCount !== 1)
|
|
40
|
+
return ['created issue counts do not follow the written issue'];
|
|
41
|
+
const archived = await client.archiveIssue(issue.id);
|
|
42
|
+
if (!archived.success || (await client.team(team.id)).issueCount !== 0 || (await client.viewer).createdIssueCount !== 1)
|
|
43
|
+
return ['archive did not update the active team count while preserving the creator count'];
|
|
44
|
+
const response = await c.fetch(`https://${c.host}/graphql`, { method: 'POST', headers: { 'content-type': 'application/json', authorization: String(apiKey) }, body: JSON.stringify({ query: 'query($id: String!) { team(id: $id) { issueCount(includeArchived: true) } }', variables: { id: team.id } }) });
|
|
45
|
+
const total = await response.json();
|
|
46
|
+
return !total.errors?.length && total.data?.team?.issueCount === 1 ? [] : ['includeArchived does not include the stored archived issue'];
|
|
47
|
+
},
|
|
48
|
+
}];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/twin-standard",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.97",
|
|
4
4
|
"description": "The Protocol 3 standard for Volter twin packs: the criteria a pack is graded by, the grader, the gate a pack repository and the twin catalog run on every change and submission, and the spec derivation the grade checks against. Pin a version: every place a pack is judged judges by that standard.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"twin",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"yaml": "2.9.0"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
|
-
"@volter/world-core": "3.0.
|
|
78
|
+
"@volter/world-core": "3.0.105",
|
|
79
79
|
"@volter/world-runtime": "*",
|
|
80
80
|
"playwright": "^1.63.0"
|
|
81
81
|
},
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// The dependency demand's official Linear SDK pin, unchanged, over the World's GraphQL routing.
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { standardPackageFile } from '../package-file.ts';
|
|
4
|
+
import type { ClientCase, ClientContext } from './harness.ts';
|
|
5
|
+
const sdk = createRequire(new URL('package.json', standardPackageFile('client-sdks/')));
|
|
6
|
+
async function setup(c: ClientContext) {
|
|
7
|
+
const door = async (path: string, body: unknown) => {
|
|
8
|
+
const answer = await c.fetch(`https://${c.host}${path}`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) });
|
|
9
|
+
if (!answer.ok) throw new Error(`${path} answered ${answer.status}: ${await answer.text()}`);
|
|
10
|
+
return await answer.json() as Record<string, unknown>;
|
|
11
|
+
};
|
|
12
|
+
const made = await door('/_twin/workspaces', { name: 'SDK workspace', urlKey: 'sdk-workspace', owner: { name: 'SDK owner', email: 'owner@linear-sdk.test', password: 'synthetic-sdk-password' } });
|
|
13
|
+
const key = await door('/_twin/api-keys', { email: 'owner@linear-sdk.test', label: 'SDK case' });
|
|
14
|
+
return { owner: made.owner, apiKey: key.key, client: new (sdk('@linear/sdk').LinearClient)({ apiKey: key.key }) };
|
|
15
|
+
}
|
|
16
|
+
export const cases: ClientCase[] = [{
|
|
17
|
+
id: 'issue-create-update-and-read',
|
|
18
|
+
asks: 'the SDK reads its full viewer/team/issue models, writes and reads an issue, and observes native counts after archive',
|
|
19
|
+
source: 'https://linear.app/developers/sdk-fetching-and-modifying-data "To create a model, call the Linear Client mutation and pass an input object:"',
|
|
20
|
+
async run(c) {
|
|
21
|
+
const { owner, apiKey, client } = await setup(c);
|
|
22
|
+
const me = await client.viewer;
|
|
23
|
+
if (me.id !== owner || me.email !== 'owner@linear-sdk.test') return ['viewer does not identify the key owner'];
|
|
24
|
+
const madeTeam = await client.createTeam({ name: 'SDK team', key: 'SDK' });
|
|
25
|
+
const team = await madeTeam.team;
|
|
26
|
+
if (!madeTeam.success || !team?.id) return ['createTeam returned no successful team'];
|
|
27
|
+
const madeIssue = await client.createIssue({ teamId: team.id, title: 'SDK issue', description: 'Synthetic issue body' });
|
|
28
|
+
const issue = await madeIssue.issue;
|
|
29
|
+
if (!madeIssue.success || !issue?.id || issue.title !== 'SDK issue') return ['createIssue returned no successful issue'];
|
|
30
|
+
const updated = await client.updateIssue(issue.id, { title: 'SDK issue updated', priority: 2 });
|
|
31
|
+
if (!updated.success) return ['updateIssue did not succeed'];
|
|
32
|
+
const got = await client.issue(issue.id);
|
|
33
|
+
if (got.id !== issue.id || got.title !== 'SDK issue updated' || got.description !== 'Synthetic issue body' || got.priority !== 2) return ['issue did not preserve its id, changed title/priority and original description'];
|
|
34
|
+
if ((await client.viewer).createdIssueCount !== 1 || (await client.team(team.id)).issueCount !== 1) return ['created issue counts do not follow the written issue'];
|
|
35
|
+
const archived = await client.archiveIssue(issue.id);
|
|
36
|
+
if (!archived.success || (await client.team(team.id)).issueCount !== 0 || (await client.viewer).createdIssueCount !== 1) return ['archive did not update the active team count while preserving the creator count'];
|
|
37
|
+
const response = await c.fetch(`https://${c.host}/graphql`, { method: 'POST', headers: { 'content-type': 'application/json', authorization: String(apiKey) }, body: JSON.stringify({ query: 'query($id: String!) { team(id: $id) { issueCount(includeArchived: true) } }', variables: { id: team.id } }) });
|
|
38
|
+
const total = await response.json() as { errors?: unknown[]; data?: { team?: { issueCount?: number } } };
|
|
39
|
+
return !total.errors?.length && total.data?.team?.issueCount === 1 ? [] : ['includeArchived does not include the stored archived issue'];
|
|
40
|
+
},
|
|
41
|
+
}];
|