@looply/sdk 0.0.0-stage → 0.2.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/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"}
@@ -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"}