@looply/sdk 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +101 -3
- package/dist/client.d.ts +430 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +494 -0
- package/dist/client.js.map +1 -0
- package/dist/errors.d.ts +107 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +119 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/views.d.ts +701 -0
- package/dist/views.d.ts.map +1 -0
- package/dist/views.js +30 -0
- package/dist/views.js.map +1 -0
- package/package.json +33 -6
package/dist/errors.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* WHAT WENT WRONG, IN A FORM A PROGRAM CAN BRANCH ON.
|
|
4
|
+
*
|
|
5
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
6
|
+
* 🔴 NO CLIENT MAY EVER PARSE AN ENGLISH MESSAGE.
|
|
7
|
+
*
|
|
8
|
+
* The server's wire shape is `{ statusCode, message }`, and the messages are
|
|
9
|
+
* good ones — controllers put real work into them, and a person reading a
|
|
10
|
+
* refusal should get a sentence rather than a symbol. But a message is written
|
|
11
|
+
* for a person and is changed for a person, so a client that branches on its
|
|
12
|
+
* text breaks the next time somebody improves the wording. That is the whole
|
|
13
|
+
* reason for this file.
|
|
14
|
+
*
|
|
15
|
+
* ⚠️ THE CODES ARE DERIVED FROM STATUS, NOT INVENTED ALONGSIDE IT. There are
|
|
16
|
+
* roughly 250 throw sites in the server and none of them carries a code today.
|
|
17
|
+
* Adding one to each would be a large, mechanical, error-prone edit whose only
|
|
18
|
+
* consumer is this file — so instead the mapping lives here, where it can be
|
|
19
|
+
* read in one screen, and a `code` the server DOES supply is preferred over the
|
|
20
|
+
* derived one. That is a narrower promise than "every refusal has a bespoke
|
|
21
|
+
* code", and it is the one that is true.
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.LooplyError = void 0;
|
|
25
|
+
exports.isRetryable = isRetryable;
|
|
26
|
+
exports.codeForStatus = codeForStatus;
|
|
27
|
+
/**
|
|
28
|
+
* Whether trying the SAME request again could plausibly succeed.
|
|
29
|
+
*
|
|
30
|
+
* ⚠️ `timeout` IS RETRYABLE AND ALSO THE MOST DANGEROUS TO RETRY, which is why
|
|
31
|
+
* every mutating call in this SDK takes an idempotency key. A timed-out request
|
|
32
|
+
* may have been executed; retrying without a key is how one Start becomes two.
|
|
33
|
+
*/
|
|
34
|
+
function isRetryable(code) {
|
|
35
|
+
return code === 'unavailable' || code === 'timeout';
|
|
36
|
+
}
|
|
37
|
+
const BY_STATUS = {
|
|
38
|
+
400: 'invalid_request',
|
|
39
|
+
401: 'unauthorized',
|
|
40
|
+
403: 'forbidden',
|
|
41
|
+
404: 'not_found',
|
|
42
|
+
409: 'conflict',
|
|
43
|
+
410: 'gone',
|
|
44
|
+
422: 'invalid_request',
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* The code for an HTTP status.
|
|
48
|
+
*
|
|
49
|
+
* Anything unmapped is `unavailable`: an unrecognised 5xx is Looply failing, and
|
|
50
|
+
* an unrecognised 4xx is a refusal this client does not model — which the caller
|
|
51
|
+
* should treat as "not now" rather than as a specific promise.
|
|
52
|
+
*/
|
|
53
|
+
function codeForStatus(status) {
|
|
54
|
+
// 🔴 AN UNMAPPED 4xx IS `unavailable`, NOT `invalid_request`, AND THE
|
|
55
|
+
// DIFFERENCE IS A CLAIM THIS CLIENT CANNOT SUPPORT.
|
|
56
|
+
//
|
|
57
|
+
// `invalid_request` says, in its own words, "the request itself was wrong;
|
|
58
|
+
// retrying it unchanged will fail identically". For a status this SDK does not
|
|
59
|
+
// model — a 429 the day a rate limit is added, a 423, a 425 — that is simply
|
|
60
|
+
// false: the request was fine and the answer was "not now". `unavailable` is
|
|
61
|
+
// the weaker and honest claim, which is what the sentence above this function
|
|
62
|
+
// has always said it does.
|
|
63
|
+
//
|
|
64
|
+
// ⚠️ THE CODE USED TO DO THE OPPOSITE OF ITS OWN DOCUMENTATION. Nothing
|
|
65
|
+
// detected it because nothing tested this function at all, and the branch was
|
|
66
|
+
// UNREACHABLE at the time: the server emitted exactly 400, 401, 403, 404, 409
|
|
67
|
+
// and 503, every one of them mapped. So it was written as hardening for the
|
|
68
|
+
// next status somebody added server-side.
|
|
69
|
+
//
|
|
70
|
+
// 🔴 THAT NEXT STATUS ARRIVED, AND THE HARDENING WAS NOT ENOUGH. The membership
|
|
71
|
+
// route answers 410 for an activation code that expired or was withdrawn, and
|
|
72
|
+
// an unmapped 410 fell to `unavailable` — retryable — which tells a caller to
|
|
73
|
+
// keep presenting a code that will never work again. The census in
|
|
74
|
+
// `errors.spec.ts` is what caught it; 410 is mapped to `gone` above, and the
|
|
75
|
+
// default below is still the honest answer for a status nobody has modelled.
|
|
76
|
+
return BY_STATUS[status] ?? 'unavailable';
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The one error type this SDK throws.
|
|
80
|
+
*
|
|
81
|
+
* 🔴 EVERY FAILURE PATH PRODUCES ONE OF THESE, including a network failure and a
|
|
82
|
+
* timeout — which are not HTTP statuses at all. A caller that has to distinguish
|
|
83
|
+
* "the server said no" from "there was no server" by catching two different
|
|
84
|
+
* shapes will eventually catch only one of them.
|
|
85
|
+
*/
|
|
86
|
+
class LooplyError extends Error {
|
|
87
|
+
status;
|
|
88
|
+
code;
|
|
89
|
+
currentVersion;
|
|
90
|
+
body;
|
|
91
|
+
constructor(init) {
|
|
92
|
+
super(init.message);
|
|
93
|
+
this.name = 'LooplyError';
|
|
94
|
+
this.status = init.status;
|
|
95
|
+
this.code = normalise(init.serverCode) ?? codeForStatus(init.status);
|
|
96
|
+
this.currentVersion = init.currentVersion;
|
|
97
|
+
this.body = init.body;
|
|
98
|
+
}
|
|
99
|
+
/** Whether trying this exact request again could plausibly succeed. */
|
|
100
|
+
get retryable() {
|
|
101
|
+
return isRetryable(this.code);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.LooplyError = LooplyError;
|
|
105
|
+
/** A server-supplied code, but only if it is one this SDK actually models. */
|
|
106
|
+
function normalise(code) {
|
|
107
|
+
const known = [
|
|
108
|
+
'unauthorized',
|
|
109
|
+
'forbidden',
|
|
110
|
+
'not_found',
|
|
111
|
+
'gone',
|
|
112
|
+
'conflict',
|
|
113
|
+
'invalid_request',
|
|
114
|
+
'unavailable',
|
|
115
|
+
'timeout',
|
|
116
|
+
];
|
|
117
|
+
return known.find((k) => k === code);
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;AAqDH,kCAEC;AAmBD,sCAwBC;AApDD;;;;;;GAMG;AACH,SAAgB,WAAW,CAAC,IAAqB;IAC/C,OAAO,IAAI,KAAK,aAAa,IAAI,IAAI,KAAK,SAAS,CAAC;AACtD,CAAC;AAED,MAAM,SAAS,GAA8C;IAC3D,GAAG,EAAE,iBAAiB;IACtB,GAAG,EAAE,cAAc;IACnB,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,UAAU;IACf,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,iBAAiB;CACvB,CAAC;AAEF;;;;;;GAMG;AACH,SAAgB,aAAa,CAAC,MAAc;IAC1C,sEAAsE;IACtE,oDAAoD;IACpD,EAAE;IACF,2EAA2E;IAC3E,+EAA+E;IAC/E,6EAA6E;IAC7E,6EAA6E;IAC7E,8EAA8E;IAC9E,2BAA2B;IAC3B,EAAE;IACF,wEAAwE;IACxE,8EAA8E;IAC9E,8EAA8E;IAC9E,4EAA4E;IAC5E,0CAA0C;IAC1C,EAAE;IACF,gFAAgF;IAChF,8EAA8E;IAC9E,8EAA8E;IAC9E,mEAAmE;IACnE,6EAA6E;IAC7E,6EAA6E;IAC7E,OAAO,SAAS,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC;AAC5C,CAAC;AAYD;;;;;;;GAOG;AACH,MAAa,WAAY,SAAQ,KAAK;IAC3B,MAAM,CAAS;IACf,IAAI,CAAkB;IACtB,cAAc,CAAqB;IACnC,IAAI,CAAU;IAEvB,YAAY,IAAqB;QAC/B,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrE,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;QAC1C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACxB,CAAC;IAED,uEAAuE;IACvE,IAAI,SAAS;QACX,OAAO,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;CACF;AAnBD,kCAmBC;AAED,8EAA8E;AAC9E,SAAS,SAAS,CAAC,IAAwB;IACzC,MAAM,KAAK,GAA+B;QACxC,cAAc;QACd,WAAW;QACX,WAAW;QACX,MAAM;QACN,UAAU;QACV,iBAAiB;QACjB,aAAa;QACb,SAAS;KACV,CAAC;IACF,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;AACvC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @looply/sdk — the stable client contract.
|
|
3
|
+
*
|
|
4
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
* 🔴 WHAT IS EXPORTED IS WHAT IS PROMISED.
|
|
6
|
+
*
|
|
7
|
+
* There is no `export *` here, and that is deliberate. A star export makes every
|
|
8
|
+
* future addition to any of these files public the moment somebody writes it,
|
|
9
|
+
* which is how an internal helper becomes a compatibility obligation nobody
|
|
10
|
+
* chose. Adding a name to this file is the act of promising it.
|
|
11
|
+
*
|
|
12
|
+
* ⚠️ NOTHING FROM `@looply/core` IS RE-EXPORTED. Core is the internal shared
|
|
13
|
+
* vocabulary of the server, the CLI and the app; it changes when they need it
|
|
14
|
+
* to. An external client that imported a core type through here would be pinned
|
|
15
|
+
* to an internal release cycle it cannot see.
|
|
16
|
+
*/
|
|
17
|
+
export { LooplyClient } from './client';
|
|
18
|
+
export type { LooplyClientOptions } from './client';
|
|
19
|
+
export { LooplyError, isRetryable, codeForStatus } from './errors';
|
|
20
|
+
export type { LooplyErrorCode, LooplyErrorInit } from './errors';
|
|
21
|
+
export type { ApprovalStateView, ApprovalView, CommandStatusView, EvidenceRefView, ExecutionStateView, GoalStateView, GoalView, GovernedExecutionView, ItemStateView, ItemView, KeeperAnswerView, KeeperCitationView, PageView, PlatformEventView, ProjectView, RunStatusView, GoalAutonomyReport, GoalAutonomyStateView, GoalAutonomyView, GoalEvidenceView, PresenceView, GoalVersionEvidenceView, EvidenceRunView, EvidenceRoundView, EvidenceGateStepView, EvidencePursuitView, EvidencePushView, DispositionKindView, CoverageKindView, EvidenceAnchorView, GoalRefView, RequirementCoverageView, RequirementLinkView, SourceCoverageView, RepositoryAnchorView, SourceAttestationView, SourceClassificationView, SourceDetailView, SourceDispositionView, SourceKindView, SourceRecordView, SourceStatementKindView, SourceStatementView, SourceView, StatementClassView, } from './views';
|
|
22
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAEpD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACnE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEjE,YAAY,EACV,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,QAAQ,EACR,qBAAqB,EACrB,aAAa,EACb,QAAQ,EACR,gBAAgB,EAChB,kBAAkB,EAClB,QAAQ,EACR,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,uBAAuB,EACvB,eAAe,EACf,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,WAAW,EACX,uBAAuB,EACvB,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,wBAAwB,EACxB,gBAAgB,EAChB,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,uBAAuB,EACvB,mBAAmB,EACnB,UAAU,EACV,kBAAkB,GACnB,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @looply/sdk — the stable client contract.
|
|
4
|
+
*
|
|
5
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
6
|
+
* 🔴 WHAT IS EXPORTED IS WHAT IS PROMISED.
|
|
7
|
+
*
|
|
8
|
+
* There is no `export *` here, and that is deliberate. A star export makes every
|
|
9
|
+
* future addition to any of these files public the moment somebody writes it,
|
|
10
|
+
* which is how an internal helper becomes a compatibility obligation nobody
|
|
11
|
+
* chose. Adding a name to this file is the act of promising it.
|
|
12
|
+
*
|
|
13
|
+
* ⚠️ NOTHING FROM `@looply/core` IS RE-EXPORTED. Core is the internal shared
|
|
14
|
+
* vocabulary of the server, the CLI and the app; it changes when they need it
|
|
15
|
+
* to. An external client that imported a core type through here would be pinned
|
|
16
|
+
* to an internal release cycle it cannot see.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.codeForStatus = exports.isRetryable = exports.LooplyError = exports.LooplyClient = void 0;
|
|
20
|
+
var client_1 = require("./client");
|
|
21
|
+
Object.defineProperty(exports, "LooplyClient", { enumerable: true, get: function () { return client_1.LooplyClient; } });
|
|
22
|
+
var errors_1 = require("./errors");
|
|
23
|
+
Object.defineProperty(exports, "LooplyError", { enumerable: true, get: function () { return errors_1.LooplyError; } });
|
|
24
|
+
Object.defineProperty(exports, "isRetryable", { enumerable: true, get: function () { return errors_1.isRetryable; } });
|
|
25
|
+
Object.defineProperty(exports, "codeForStatus", { enumerable: true, get: function () { return errors_1.codeForStatus; } });
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;AAEH,mCAAwC;AAA/B,sGAAA,YAAY,OAAA;AAGrB,mCAAmE;AAA1D,qGAAA,WAAW,OAAA;AAAE,qGAAA,WAAW,OAAA;AAAE,uGAAA,aAAa,OAAA"}
|