@sasjs/api 0.4.0 → 0.5.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/README.md +10 -12
- package/build/browser.js +179 -53
- package/build/cjs/host/dispatch.js +7 -2
- package/build/cjs/routes/index.js +8 -2
- package/build/cjs/routes/overlay.js +171 -48
- package/build/esm/host/dispatch.d.ts.map +1 -1
- package/build/esm/host/dispatch.js +7 -2
- package/build/esm/routes/index.d.ts.map +1 -1
- package/build/esm/routes/index.js +8 -2
- package/build/esm/routes/overlay.d.ts +26 -3
- package/build/esm/routes/overlay.d.ts.map +1 -1
- package/build/esm/routes/overlay.js +171 -48
- package/build/esm/types.d.ts +7 -0
- package/build/esm/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,9 +9,10 @@ The plan that this package implements is tracked in `sasjs/cli#1550`.
|
|
|
9
9
|
|
|
10
10
|
## What is in it
|
|
11
11
|
|
|
12
|
-
**The route table** (`ROUTES`, `findRoute`) is every operation in the API
|
|
12
|
+
**The route table** (`ROUTES`, `findRoute`) is every operation in the API, derived from `spec/swagger.yaml` by `npm run sync:routes`.
|
|
13
13
|
The statuses come from that document and nowhere else: the overlay cannot add one, and an overlay entry naming a status the document does not state fails the build rather than describing a wish.
|
|
14
|
-
The document cannot say everything a host needs, so the reviewed overlay adds the body each response carries, the capability a route needs, and
|
|
14
|
+
The document cannot say everything a host needs, so the reviewed overlay adds the body each response carries, the capability a route needs, and the decision for the operation: `implemented: true`, or `implemented: false` with the reason it is not.
|
|
15
|
+
Every operation has an entry - the table refuses one that does not, so "nobody has looked at this yet" cannot hide as silence - and a host asked for an unimplemented route answers `501` carrying that reason.
|
|
15
16
|
Where the document is wrong, it is corrected in `sasjs/server` and re-vendored here - never worked around in the overlay.
|
|
16
17
|
|
|
17
18
|
### Regenerating the vendored document
|
|
@@ -67,7 +68,7 @@ Both hosts answer with the same shapes:
|
|
|
67
68
|
|
|
68
69
|
- a successful write is `{ status: 'success' }`;
|
|
69
70
|
- a refusal is an `ApiError` carrying `status` and `message`, which a host renders as its own transport requires (`res.status(code).send(...)` on the server, a `Response` in the worker);
|
|
70
|
-
- an operation this package has no handler for is `501`, naming the operation - not a `404` that looks like a typo in the caller;
|
|
71
|
+
- an operation this package has no handler for is `501`, naming the operation and the reason it is not implemented here - not a `404` that looks like a typo in the caller;
|
|
71
72
|
- an operation the API does not have is `404`.
|
|
72
73
|
|
|
73
74
|
## Guarantees the build enforces
|
|
@@ -75,17 +76,14 @@ Both hosts answer with the same shapes:
|
|
|
75
76
|
- **No runtime imports.** `src` compiles with `"types": []` and `lib: ES2020`, so a `Buffer`, a `process` or a `document` reference fails `npm run build` rather than a worker at run time. Nothing node-shaped can creep into the browser path.
|
|
76
77
|
- **No Node-only APIs in the published surface.** Storage is a port (`DriveStore`), and `MemoryDriveStore` ships so a host without a filesystem has an implementation to use.
|
|
77
78
|
- **Three builds from one source.** `build/cjs` for node, `build/esm` for a bundler (with `build/esm` marked `"type": "module"` by the build script so Node runs it as ESM), and `build/browser.js` - a classic-worker IIFE that assigns `self.SASjsApi` - for a service worker that has only `importScripts`. CI loads all three.
|
|
78
|
-
- **The table cannot rot silently.** `npm run check:routes` fails when `src/routes/generated.ts` and `spec/swagger.yaml` disagree, and the module throws at import when an overlay entry names an operation the document does not have.
|
|
79
|
+
- **The table cannot rot silently.** `npm run check:routes` fails when `src/routes/generated.ts` and `spec/swagger.yaml` disagree, and the module throws at import when an overlay entry names an operation the document does not have, or when an operation has no entry at all.
|
|
79
80
|
|
|
80
81
|
## Status
|
|
81
82
|
|
|
82
|
-
|
|
83
|
+
`src/routes/overlay.ts` is the status, and the table refuses an operation that is not in it: each entry is `implemented: true` - the package answers the route - or `implemented: false` with the dependency it waits on.
|
|
84
|
+
`ROUTES.filter((route) => !route.implemented)` is therefore the list of what is left, each row naming the port, host decision or plan it is waiting for; a host asked for one answers `501` with that reason.
|
|
83
85
|
|
|
84
|
-
|
|
85
|
-
- the seven sign-in handlers - `auth/token`, `auth/refresh`, `auth/logout`, `SASLogon/login`, `SASLogon/authorize`, `SASLogon/logout`, and the session read.
|
|
86
|
-
|
|
87
|
-
Not implemented yet, in the order the plan sets: the rest of identity (`user`, `group`, `permission`, `auth/updatePassword`, `info/authorizedRoutes`), the session record behind `log`/`output`, program execution (`stp`, `code`), `drive/deploy` and its size cap, and the admin routes.
|
|
88
|
-
`src/routes/overlay.ts` is the list: an operation with `implemented: true` has a handler, and everything else answers `501` with its own name.
|
|
86
|
+
Every implemented operation is exercised by the corpus, and a handler without a case fails `spec/corpus-parity.spec.ts` - so "implemented" and "proven against the contract" are the same list rather than two that drift.
|
|
89
87
|
|
|
90
88
|
A status the document states but only a host can send - an account lockout (`429`), a provider failure (`500`), a session-store failure - is marked `hostResponses` in the overlay. It stays in the document, because a client still receives it, and is excluded from the parity check, whose other side is what this package answers; the host's own suite pins it.
|
|
91
89
|
|
|
@@ -107,9 +105,9 @@ npm run check:routes # the generated table matches spec/swagger.yaml
|
|
|
107
105
|
npm run lint
|
|
108
106
|
```
|
|
109
107
|
|
|
110
|
-
Refreshing the API surface: regenerate `api/public/swagger.yaml` in `sasjs/server` (`npm run swagger`), copy it to `spec/swagger.yaml`, and run `npm run sync:routes`. Review the diff
|
|
108
|
+
Refreshing the API surface: regenerate `api/public/swagger.yaml` in `sasjs/server` (`npm run swagger`), copy it to `spec/swagger.yaml`, and run `npm run sync:routes`. Review the diff: a new operation needs an overlay entry, and the table refuses to build without one.
|
|
111
109
|
|
|
112
|
-
Re-vendoring is a two-repository change and the server half merges first: the document is only true once the controllers it is generated from answer what it states, so a copy taken from an unmerged server branch describes a server that does not exist yet.
|
|
110
|
+
Re-vendoring is a two-repository change and the server half merges first: the document is only true once the controllers it is generated from answer what it states, so a copy taken from an unmerged server branch describes a server that does not exist yet. Take the copy from a released tag of `sasjs/server` rather than from its `main`, and the document and the code a host runs cannot disagree.
|
|
113
111
|
|
|
114
112
|
## Licence
|
|
115
113
|
|
package/build/browser.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* @sasjs/api 0.
|
|
1
|
+
/* @sasjs/api 0.5.0 - the SASjs Server API, for a service worker. https://github.com/sasjs/api */
|
|
2
2
|
"use strict";
|
|
3
3
|
var SASjsApi = (() => {
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -2070,6 +2070,16 @@ var SASjsApi = (() => {
|
|
|
2070
2070
|
Client: ["identity"],
|
|
2071
2071
|
Web: ["identity"]
|
|
2072
2072
|
};
|
|
2073
|
+
var IDENTITY_RECORDS = "The identity service answers the sign-in flow. User, group, permission rule, client and provider records need read and write methods on the same store, which this package does not have yet.";
|
|
2074
|
+
var EDITOR = "Linting and formatting come from @sasjs/lint, which this package does not depend on. They land with the code routes so an editor has one wiring.";
|
|
2075
|
+
var RUNTIME = "Starting a program needs a runtime port, which this package does not have: a host decides which languages it holds, and answers this itself until then.";
|
|
2076
|
+
var SESSION_RECORD = "A triggered session's record, log, output and state belong to the runtime that ran the program, and this package has no runtime port.";
|
|
2077
|
+
var HOST_CONFIG = "The body is the host's own configuration - ports, run times and providers - which a mock states for itself and this package cannot know.";
|
|
2078
|
+
var PERMISSIONS = "The route inventory is the host's permission model, which this package does not hold.";
|
|
2079
|
+
var HOST_OWNED = "The host owns this report: it describes the host's own process and files, which a browser build has no equivalent of.";
|
|
2080
|
+
var WEB_BUNDLE = "The host serves its own web bundle; there is no API decision here for this package to answer.";
|
|
2081
|
+
var DEPLOY_CAP = "Writing a whole tree in one request can exhaust the host's storage, so the deploy routes land with the size cap that bounds it.";
|
|
2082
|
+
var UNPUBLISH = "Unpublishing inverts the deploy route, so the two land together.";
|
|
2073
2083
|
var success = { status: "success" };
|
|
2074
2084
|
var user = {
|
|
2075
2085
|
uid: "",
|
|
@@ -2079,11 +2089,61 @@ var SASjsApi = (() => {
|
|
|
2079
2089
|
needsToUpdatePassword: false
|
|
2080
2090
|
};
|
|
2081
2091
|
var OVERLAY = {
|
|
2082
|
-
//
|
|
2083
|
-
|
|
2084
|
-
"
|
|
2085
|
-
|
|
2086
|
-
|
|
2092
|
+
// Web - the sign-in pages the identity service answers, and the bundle itself
|
|
2093
|
+
"GET /": { implemented: false, reason: WEB_BUNDLE },
|
|
2094
|
+
"POST /SASLogon/login": {
|
|
2095
|
+
implemented: true,
|
|
2096
|
+
// The lockout and the provider failure are answered before the package is
|
|
2097
|
+
// consulted (routes/web/web.ts in sasjs/server).
|
|
2098
|
+
hostResponses: [429, 500],
|
|
2099
|
+
responses: { 200: { loggedIn: true, user } }
|
|
2100
|
+
},
|
|
2101
|
+
"POST /SASLogon/authorize": {
|
|
2102
|
+
implemented: true,
|
|
2103
|
+
responses: { 200: { code: "" } }
|
|
2104
|
+
},
|
|
2105
|
+
"GET /SASLogon/logout": {
|
|
2106
|
+
implemented: true,
|
|
2107
|
+
hostResponses: [500],
|
|
2108
|
+
responses: { 200: "OK!" }
|
|
2109
|
+
},
|
|
2110
|
+
// Admin - the host's own process and files
|
|
2111
|
+
"GET /SASjsApi/admin/diagnostics": { implemented: false, reason: HOST_OWNED },
|
|
2112
|
+
"GET /SASjsApi/admin/logs": { implemented: false, reason: HOST_OWNED },
|
|
2113
|
+
"GET /SASjsApi/admin/logs/{fileName}": {
|
|
2114
|
+
implemented: false,
|
|
2115
|
+
reason: HOST_OWNED
|
|
2116
|
+
},
|
|
2117
|
+
// Auth - the token flow, plus a password change the identity store cannot write
|
|
2118
|
+
"POST /SASjsApi/auth/token": {
|
|
2119
|
+
implemented: true,
|
|
2120
|
+
responses: { 200: { accessToken: "", refreshToken: "" } }
|
|
2121
|
+
},
|
|
2122
|
+
"POST /SASjsApi/auth/refresh": {
|
|
2123
|
+
implemented: true,
|
|
2124
|
+
responses: { 200: { accessToken: "", refreshToken: "" } }
|
|
2125
|
+
},
|
|
2126
|
+
"POST /SASjsApi/auth/logout": { implemented: true },
|
|
2127
|
+
"PATCH /SASjsApi/auth/updatePassword": {
|
|
2128
|
+
implemented: false,
|
|
2129
|
+
reason: IDENTITY_RECORDS
|
|
2130
|
+
},
|
|
2131
|
+
// Auth Config - the providers a host was started with, and an LDAP import
|
|
2132
|
+
"GET /SASjsApi/authConfig": { implemented: false, reason: HOST_CONFIG },
|
|
2133
|
+
"POST /SASjsApi/authConfig/synchroniseWithLDAP": {
|
|
2134
|
+
implemented: false,
|
|
2135
|
+
reason: IDENTITY_RECORDS
|
|
2136
|
+
},
|
|
2137
|
+
// Client - the OAuth clients the identity store already holds for sign-in
|
|
2138
|
+
"GET /SASjsApi/client": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2139
|
+
"POST /SASjsApi/client": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2140
|
+
// Code - the editor's three calls, and the runtime one
|
|
2141
|
+
"POST /SASjsApi/code/execute": { implemented: false, reason: RUNTIME },
|
|
2142
|
+
"POST /SASjsApi/code/trigger": { implemented: false, reason: RUNTIME },
|
|
2143
|
+
"POST /SASjsApi/code/lint": { implemented: false, reason: EDITOR },
|
|
2144
|
+
"POST /SASjsApi/code/format": { implemented: false, reason: EDITOR },
|
|
2145
|
+
// Drive - the file and folder rules, and the three deploy routes
|
|
2146
|
+
// The body is the file itself, so there is nothing to annotate
|
|
2087
2147
|
"GET /SASjsApi/drive/file": { implemented: true },
|
|
2088
2148
|
"POST /SASjsApi/drive/file": {
|
|
2089
2149
|
implemented: true,
|
|
@@ -2121,60 +2181,120 @@ var SASjsApi = (() => {
|
|
|
2121
2181
|
implemented: true,
|
|
2122
2182
|
responses: { 200: { status: "success", tree: {} } }
|
|
2123
2183
|
},
|
|
2124
|
-
|
|
2125
|
-
// ones where a caller can exhaust the host's storage, so they carry a cap -
|
|
2126
|
-
// see the plan in sasjs/cli#1550 - and they are not implemented yet.
|
|
2127
|
-
"POST /SASjsApi/drive/deploy": {
|
|
2128
|
-
implemented: false
|
|
2129
|
-
},
|
|
2184
|
+
"POST /SASjsApi/drive/deploy": { implemented: false, reason: DEPLOY_CAP },
|
|
2130
2185
|
"POST /SASjsApi/drive/deploy/upload": {
|
|
2131
|
-
implemented: false
|
|
2132
|
-
|
|
2133
|
-
// Unpublishing is the counterpart of the two above and lands with them: the
|
|
2134
|
-
// document in sasjs/server states it (sasjs/server#543), so a host may answer
|
|
2135
|
-
// it in the meantime, but this package cannot until the publish side it would
|
|
2136
|
-
// invert is implemented here. The statuses are the document's; `404` is a pair
|
|
2137
|
-
// - the registry name and the appLoc it was published from - that matches no
|
|
2138
|
-
// entry, and the drive's content is deliberately left alone.
|
|
2139
|
-
"DELETE /SASjsApi/drive/deploy": {
|
|
2140
|
-
implemented: false
|
|
2141
|
-
},
|
|
2142
|
-
// The lockout and the provider failure are answered before the package is
|
|
2143
|
-
// consulted (routes/web/web.ts in sasjs/server).
|
|
2144
|
-
"POST /SASLogon/login": {
|
|
2145
|
-
implemented: true,
|
|
2146
|
-
hostResponses: [429, 500],
|
|
2147
|
-
responses: { 200: { loggedIn: true, user } }
|
|
2148
|
-
},
|
|
2149
|
-
// The sign-in flow. The statuses are the document's; these entries state the
|
|
2150
|
-
// body a client keys on, and that this package answers the operation.
|
|
2151
|
-
"POST /SASjsApi/auth/token": {
|
|
2152
|
-
implemented: true,
|
|
2153
|
-
responses: { 200: { accessToken: "", refreshToken: "" } }
|
|
2154
|
-
},
|
|
2155
|
-
"POST /SASjsApi/auth/refresh": {
|
|
2156
|
-
implemented: true,
|
|
2157
|
-
responses: { 200: { accessToken: "", refreshToken: "" } }
|
|
2158
|
-
},
|
|
2159
|
-
"POST /SASjsApi/auth/logout": { implemented: true },
|
|
2160
|
-
"POST /SASLogon/authorize": {
|
|
2161
|
-
implemented: true,
|
|
2162
|
-
responses: { 200: { code: "" } }
|
|
2163
|
-
},
|
|
2164
|
-
"GET /SASLogon/logout": {
|
|
2165
|
-
implemented: true,
|
|
2166
|
-
hostResponses: [500],
|
|
2167
|
-
responses: { 200: "OK!" }
|
|
2186
|
+
implemented: false,
|
|
2187
|
+
reason: DEPLOY_CAP
|
|
2168
2188
|
},
|
|
2189
|
+
// `404` is a pair - the registry name and the appLoc it was published from -
|
|
2190
|
+
// that matches no entry, and the drive's content is deliberately left alone.
|
|
2191
|
+
"DELETE /SASjsApi/drive/deploy": {
|
|
2192
|
+
implemented: false,
|
|
2193
|
+
reason: UNPUBLISH
|
|
2194
|
+
},
|
|
2195
|
+
// Group - members and the groups themselves
|
|
2196
|
+
"GET /SASjsApi/group": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2197
|
+
"GET /SASjsApi/group/{uid}": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2198
|
+
"GET /SASjsApi/group/by/groupname/{name}": {
|
|
2199
|
+
implemented: false,
|
|
2200
|
+
reason: IDENTITY_RECORDS
|
|
2201
|
+
},
|
|
2202
|
+
"POST /SASjsApi/group": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2203
|
+
"POST /SASjsApi/group/{groupUid}/{userUid}": {
|
|
2204
|
+
implemented: false,
|
|
2205
|
+
reason: IDENTITY_RECORDS
|
|
2206
|
+
},
|
|
2207
|
+
"DELETE /SASjsApi/group/{groupUid}/{userUid}": {
|
|
2208
|
+
implemented: false,
|
|
2209
|
+
reason: IDENTITY_RECORDS
|
|
2210
|
+
},
|
|
2211
|
+
"DELETE /SASjsApi/group/{uid}": {
|
|
2212
|
+
implemented: false,
|
|
2213
|
+
reason: IDENTITY_RECORDS
|
|
2214
|
+
},
|
|
2215
|
+
// Info - what the host is, and what this caller may use
|
|
2216
|
+
"GET /SASjsApi/info": { implemented: false, reason: HOST_CONFIG },
|
|
2217
|
+
"GET /SASjsApi/info/authorizedRoutes": {
|
|
2218
|
+
implemented: false,
|
|
2219
|
+
// the route inventory reports what the CALLER may use, so it needs an
|
|
2220
|
+
// identity even though the rest of the Info tag needs nothing
|
|
2221
|
+
capabilities: ["identity"],
|
|
2222
|
+
reason: PERMISSIONS
|
|
2223
|
+
},
|
|
2224
|
+
"GET /SASjsApi/info/myAuthorizedRoutes": {
|
|
2225
|
+
implemented: false,
|
|
2226
|
+
capabilities: ["identity"],
|
|
2227
|
+
reason: PERMISSIONS
|
|
2228
|
+
},
|
|
2229
|
+
// Permission - the rules the inventory above is derived from
|
|
2230
|
+
"GET /SASjsApi/permission": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2231
|
+
"POST /SASjsApi/permission": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2232
|
+
"PATCH /SASjsApi/permission/{uid}": {
|
|
2233
|
+
implemented: false,
|
|
2234
|
+
reason: IDENTITY_RECORDS
|
|
2235
|
+
},
|
|
2236
|
+
"DELETE /SASjsApi/permission/{uid}": {
|
|
2237
|
+
implemented: false,
|
|
2238
|
+
reason: IDENTITY_RECORDS
|
|
2239
|
+
},
|
|
2240
|
+
// Session - the caller's own session, then the record behind a triggered one
|
|
2169
2241
|
"GET /SASjsApi/session": {
|
|
2170
2242
|
implemented: true,
|
|
2171
2243
|
responses: { 200: user }
|
|
2244
|
+
},
|
|
2245
|
+
"GET /SASjsApi/session/{sessionId}": {
|
|
2246
|
+
implemented: false,
|
|
2247
|
+
reason: SESSION_RECORD
|
|
2248
|
+
},
|
|
2249
|
+
"GET /SASjsApi/session/{sessionId}/log": {
|
|
2250
|
+
implemented: false,
|
|
2251
|
+
reason: SESSION_RECORD
|
|
2252
|
+
},
|
|
2253
|
+
"GET /SASjsApi/session/{sessionId}/output": {
|
|
2254
|
+
implemented: false,
|
|
2255
|
+
reason: SESSION_RECORD
|
|
2256
|
+
},
|
|
2257
|
+
"GET /SASjsApi/session/{sessionId}/state": {
|
|
2258
|
+
implemented: false,
|
|
2259
|
+
reason: SESSION_RECORD
|
|
2260
|
+
},
|
|
2261
|
+
// STP - a stored program, on the runtime port
|
|
2262
|
+
"GET /SASjsApi/stp/execute": { implemented: false, reason: RUNTIME },
|
|
2263
|
+
"POST /SASjsApi/stp/execute": { implemented: false, reason: RUNTIME },
|
|
2264
|
+
"POST /SASjsApi/stp/trigger": { implemented: false, reason: RUNTIME },
|
|
2265
|
+
// User - the accounts the identity store already holds for sign-in
|
|
2266
|
+
"GET /SASjsApi/user": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2267
|
+
"GET /SASjsApi/user/{uid}": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2268
|
+
"GET /SASjsApi/user/by/username/{username}": {
|
|
2269
|
+
implemented: false,
|
|
2270
|
+
reason: IDENTITY_RECORDS
|
|
2271
|
+
},
|
|
2272
|
+
"POST /SASjsApi/user": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2273
|
+
"PATCH /SASjsApi/user/{uid}": {
|
|
2274
|
+
implemented: false,
|
|
2275
|
+
reason: IDENTITY_RECORDS
|
|
2276
|
+
},
|
|
2277
|
+
"PATCH /SASjsApi/user/by/username/{username}": {
|
|
2278
|
+
implemented: false,
|
|
2279
|
+
reason: IDENTITY_RECORDS
|
|
2280
|
+
},
|
|
2281
|
+
"DELETE /SASjsApi/user/{uid}": {
|
|
2282
|
+
implemented: false,
|
|
2283
|
+
reason: IDENTITY_RECORDS
|
|
2284
|
+
},
|
|
2285
|
+
"DELETE /SASjsApi/user/by/username/{username}": {
|
|
2286
|
+
implemented: false,
|
|
2287
|
+
reason: IDENTITY_RECORDS
|
|
2172
2288
|
}
|
|
2173
2289
|
};
|
|
2174
2290
|
|
|
2175
2291
|
// src/routes/index.ts
|
|
2176
2292
|
var merge = (generated) => {
|
|
2177
|
-
const overlay = OVERLAY[generated.id]
|
|
2293
|
+
const overlay = OVERLAY[generated.id];
|
|
2294
|
+
if (!overlay)
|
|
2295
|
+
throw new Error(
|
|
2296
|
+
`src/routes/overlay.ts has no entry for ${generated.id}. Every operation states whether this package implements it and, when it does not, the reason it does not: add \`{ implemented: false, reason: '...' }\` until a handler exists.`
|
|
2297
|
+
);
|
|
2178
2298
|
const documented = new Map(
|
|
2179
2299
|
generated.responses.map((r) => [r.status, r.description])
|
|
2180
2300
|
);
|
|
@@ -2203,7 +2323,8 @@ var SASjsApi = (() => {
|
|
|
2203
2323
|
responses,
|
|
2204
2324
|
hostResponses: overlay.hostResponses || [],
|
|
2205
2325
|
capabilities: overlay.capabilities || TAG_CAPABILITIES[generated.tag] || [],
|
|
2206
|
-
implemented: overlay.implemented
|
|
2326
|
+
implemented: overlay.implemented,
|
|
2327
|
+
...overlay.implemented ? {} : { reason: overlay.reason }
|
|
2207
2328
|
};
|
|
2208
2329
|
};
|
|
2209
2330
|
var ROUTES = GENERATED_ROUTES.map(merge);
|
|
@@ -2367,8 +2488,13 @@ var SASjsApi = (() => {
|
|
|
2367
2488
|
};
|
|
2368
2489
|
case "GET /SASjsApi/drive/filetree":
|
|
2369
2490
|
return { status: 200, body: await drive.tree(query._folderPath) };
|
|
2370
|
-
default:
|
|
2371
|
-
|
|
2491
|
+
default: {
|
|
2492
|
+
const reason = routeById(routeId)?.reason;
|
|
2493
|
+
throw new ApiError(
|
|
2494
|
+
501,
|
|
2495
|
+
`This package has no handler for ${routeId}.${reason ? ` ${reason}` : ""}`
|
|
2496
|
+
);
|
|
2497
|
+
}
|
|
2372
2498
|
}
|
|
2373
2499
|
};
|
|
2374
2500
|
var HANDLED_ROUTES = [
|
|
@@ -131,8 +131,13 @@ const dispatch = async (services, routeId, request = {}) => {
|
|
|
131
131
|
};
|
|
132
132
|
case 'GET /SASjsApi/drive/filetree':
|
|
133
133
|
return { status: 200, body: await drive.tree(query._folderPath) };
|
|
134
|
-
default:
|
|
135
|
-
|
|
134
|
+
default: {
|
|
135
|
+
// The reason the table carries travels with the refusal: a host developer
|
|
136
|
+
// reading a 501 wants to know what the operation waits on, not only that
|
|
137
|
+
// it is missing.
|
|
138
|
+
const reason = (0, index_js_1.routeById)(routeId)?.reason;
|
|
139
|
+
throw new types_js_1.ApiError(501, `This package has no handler for ${routeId}.${reason ? ` ${reason}` : ''}`);
|
|
140
|
+
}
|
|
136
141
|
}
|
|
137
142
|
};
|
|
138
143
|
exports.dispatch = dispatch;
|
|
@@ -13,7 +13,12 @@ exports.findRoute = exports.routeById = exports.ROUTES = void 0;
|
|
|
13
13
|
const generated_js_1 = require("./generated.js");
|
|
14
14
|
const overlay_js_1 = require("./overlay.js");
|
|
15
15
|
const merge = (generated) => {
|
|
16
|
-
const overlay = overlay_js_1.OVERLAY[generated.id]
|
|
16
|
+
const overlay = overlay_js_1.OVERLAY[generated.id];
|
|
17
|
+
// Every operation states its decision. A route with no entry is a route
|
|
18
|
+
// nobody has looked at, which reads exactly like a route that is about to be
|
|
19
|
+
// done - so it fails the build instead, and the message says what to add.
|
|
20
|
+
if (!overlay)
|
|
21
|
+
throw new Error(`src/routes/overlay.ts has no entry for ${generated.id}. Every operation states whether this package implements it and, when it does not, the reason it does not: add \`{ implemented: false, reason: '...' }\` until a handler exists.`);
|
|
17
22
|
const documented = new Map(generated.responses.map((r) => [r.status, r.description]));
|
|
18
23
|
// The document states the statuses and the overlay only annotates the ones a
|
|
19
24
|
// client keys a body on. Naming a status the document does not state is a
|
|
@@ -46,7 +51,8 @@ const merge = (generated) => {
|
|
|
46
51
|
responses,
|
|
47
52
|
hostResponses: overlay.hostResponses || [],
|
|
48
53
|
capabilities: overlay.capabilities || overlay_js_1.TAG_CAPABILITIES[generated.tag] || [],
|
|
49
|
-
implemented: overlay.implemented
|
|
54
|
+
implemented: overlay.implemented,
|
|
55
|
+
...(overlay.implemented ? {} : { reason: overlay.reason })
|
|
50
56
|
};
|
|
51
57
|
};
|
|
52
58
|
exports.ROUTES = generated_js_1.GENERATED_ROUTES.map(merge);
|
|
@@ -17,6 +17,23 @@ exports.TAG_CAPABILITIES = {
|
|
|
17
17
|
Client: ['identity'],
|
|
18
18
|
Web: ['identity']
|
|
19
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* Why a block of operations is not implemented here.
|
|
22
|
+
*
|
|
23
|
+
* One per dependency rather than one per route: the reason states what the
|
|
24
|
+
* operation waits on, and thirty copies of the same sentence would say the same
|
|
25
|
+
* thing thirty times.
|
|
26
|
+
*/
|
|
27
|
+
const IDENTITY_RECORDS = 'The identity service answers the sign-in flow. User, group, permission rule, client and provider records need read and write methods on the same store, which this package does not have yet.';
|
|
28
|
+
const EDITOR = 'Linting and formatting come from @sasjs/lint, which this package does not depend on. They land with the code routes so an editor has one wiring.';
|
|
29
|
+
const RUNTIME = 'Starting a program needs a runtime port, which this package does not have: a host decides which languages it holds, and answers this itself until then.';
|
|
30
|
+
const SESSION_RECORD = "A triggered session's record, log, output and state belong to the runtime that ran the program, and this package has no runtime port.";
|
|
31
|
+
const HOST_CONFIG = "The body is the host's own configuration - ports, run times and providers - which a mock states for itself and this package cannot know.";
|
|
32
|
+
const PERMISSIONS = "The route inventory is the host's permission model, which this package does not hold.";
|
|
33
|
+
const HOST_OWNED = "The host owns this report: it describes the host's own process and files, which a browser build has no equivalent of.";
|
|
34
|
+
const WEB_BUNDLE = 'The host serves its own web bundle; there is no API decision here for this package to answer.';
|
|
35
|
+
const DEPLOY_CAP = "Writing a whole tree in one request can exhaust the host's storage, so the deploy routes land with the size cap that bounds it.";
|
|
36
|
+
const UNPUBLISH = 'Unpublishing inverts the deploy route, so the two land together.';
|
|
20
37
|
const success = { status: 'success' };
|
|
21
38
|
const user = {
|
|
22
39
|
uid: '',
|
|
@@ -26,11 +43,61 @@ const user = {
|
|
|
26
43
|
needsToUpdatePassword: false
|
|
27
44
|
};
|
|
28
45
|
exports.OVERLAY = {
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
'
|
|
32
|
-
|
|
33
|
-
|
|
46
|
+
// Web - the sign-in pages the identity service answers, and the bundle itself
|
|
47
|
+
'GET /': { implemented: false, reason: WEB_BUNDLE },
|
|
48
|
+
'POST /SASLogon/login': {
|
|
49
|
+
implemented: true,
|
|
50
|
+
// The lockout and the provider failure are answered before the package is
|
|
51
|
+
// consulted (routes/web/web.ts in sasjs/server).
|
|
52
|
+
hostResponses: [429, 500],
|
|
53
|
+
responses: { 200: { loggedIn: true, user } }
|
|
54
|
+
},
|
|
55
|
+
'POST /SASLogon/authorize': {
|
|
56
|
+
implemented: true,
|
|
57
|
+
responses: { 200: { code: '' } }
|
|
58
|
+
},
|
|
59
|
+
'GET /SASLogon/logout': {
|
|
60
|
+
implemented: true,
|
|
61
|
+
hostResponses: [500],
|
|
62
|
+
responses: { 200: 'OK!' }
|
|
63
|
+
},
|
|
64
|
+
// Admin - the host's own process and files
|
|
65
|
+
'GET /SASjsApi/admin/diagnostics': { implemented: false, reason: HOST_OWNED },
|
|
66
|
+
'GET /SASjsApi/admin/logs': { implemented: false, reason: HOST_OWNED },
|
|
67
|
+
'GET /SASjsApi/admin/logs/{fileName}': {
|
|
68
|
+
implemented: false,
|
|
69
|
+
reason: HOST_OWNED
|
|
70
|
+
},
|
|
71
|
+
// Auth - the token flow, plus a password change the identity store cannot write
|
|
72
|
+
'POST /SASjsApi/auth/token': {
|
|
73
|
+
implemented: true,
|
|
74
|
+
responses: { 200: { accessToken: '', refreshToken: '' } }
|
|
75
|
+
},
|
|
76
|
+
'POST /SASjsApi/auth/refresh': {
|
|
77
|
+
implemented: true,
|
|
78
|
+
responses: { 200: { accessToken: '', refreshToken: '' } }
|
|
79
|
+
},
|
|
80
|
+
'POST /SASjsApi/auth/logout': { implemented: true },
|
|
81
|
+
'PATCH /SASjsApi/auth/updatePassword': {
|
|
82
|
+
implemented: false,
|
|
83
|
+
reason: IDENTITY_RECORDS
|
|
84
|
+
},
|
|
85
|
+
// Auth Config - the providers a host was started with, and an LDAP import
|
|
86
|
+
'GET /SASjsApi/authConfig': { implemented: false, reason: HOST_CONFIG },
|
|
87
|
+
'POST /SASjsApi/authConfig/synchroniseWithLDAP': {
|
|
88
|
+
implemented: false,
|
|
89
|
+
reason: IDENTITY_RECORDS
|
|
90
|
+
},
|
|
91
|
+
// Client - the OAuth clients the identity store already holds for sign-in
|
|
92
|
+
'GET /SASjsApi/client': { implemented: false, reason: IDENTITY_RECORDS },
|
|
93
|
+
'POST /SASjsApi/client': { implemented: false, reason: IDENTITY_RECORDS },
|
|
94
|
+
// Code - the editor's three calls, and the runtime one
|
|
95
|
+
'POST /SASjsApi/code/execute': { implemented: false, reason: RUNTIME },
|
|
96
|
+
'POST /SASjsApi/code/trigger': { implemented: false, reason: RUNTIME },
|
|
97
|
+
'POST /SASjsApi/code/lint': { implemented: false, reason: EDITOR },
|
|
98
|
+
'POST /SASjsApi/code/format': { implemented: false, reason: EDITOR },
|
|
99
|
+
// Drive - the file and folder rules, and the three deploy routes
|
|
100
|
+
// The body is the file itself, so there is nothing to annotate
|
|
34
101
|
'GET /SASjsApi/drive/file': { implemented: true },
|
|
35
102
|
'POST /SASjsApi/drive/file': {
|
|
36
103
|
implemented: true,
|
|
@@ -68,54 +135,110 @@ exports.OVERLAY = {
|
|
|
68
135
|
implemented: true,
|
|
69
136
|
responses: { 200: { status: 'success', tree: {} } }
|
|
70
137
|
},
|
|
71
|
-
|
|
72
|
-
// ones where a caller can exhaust the host's storage, so they carry a cap -
|
|
73
|
-
// see the plan in sasjs/cli#1550 - and they are not implemented yet.
|
|
74
|
-
'POST /SASjsApi/drive/deploy': {
|
|
75
|
-
implemented: false
|
|
76
|
-
},
|
|
138
|
+
'POST /SASjsApi/drive/deploy': { implemented: false, reason: DEPLOY_CAP },
|
|
77
139
|
'POST /SASjsApi/drive/deploy/upload': {
|
|
78
|
-
implemented: false
|
|
79
|
-
|
|
80
|
-
// Unpublishing is the counterpart of the two above and lands with them: the
|
|
81
|
-
// document in sasjs/server states it (sasjs/server#543), so a host may answer
|
|
82
|
-
// it in the meantime, but this package cannot until the publish side it would
|
|
83
|
-
// invert is implemented here. The statuses are the document's; `404` is a pair
|
|
84
|
-
// - the registry name and the appLoc it was published from - that matches no
|
|
85
|
-
// entry, and the drive's content is deliberately left alone.
|
|
86
|
-
'DELETE /SASjsApi/drive/deploy': {
|
|
87
|
-
implemented: false
|
|
88
|
-
},
|
|
89
|
-
// The lockout and the provider failure are answered before the package is
|
|
90
|
-
// consulted (routes/web/web.ts in sasjs/server).
|
|
91
|
-
'POST /SASLogon/login': {
|
|
92
|
-
implemented: true,
|
|
93
|
-
hostResponses: [429, 500],
|
|
94
|
-
responses: { 200: { loggedIn: true, user } }
|
|
95
|
-
},
|
|
96
|
-
// The sign-in flow. The statuses are the document's; these entries state the
|
|
97
|
-
// body a client keys on, and that this package answers the operation.
|
|
98
|
-
'POST /SASjsApi/auth/token': {
|
|
99
|
-
implemented: true,
|
|
100
|
-
responses: { 200: { accessToken: '', refreshToken: '' } }
|
|
101
|
-
},
|
|
102
|
-
'POST /SASjsApi/auth/refresh': {
|
|
103
|
-
implemented: true,
|
|
104
|
-
responses: { 200: { accessToken: '', refreshToken: '' } }
|
|
105
|
-
},
|
|
106
|
-
'POST /SASjsApi/auth/logout': { implemented: true },
|
|
107
|
-
'POST /SASLogon/authorize': {
|
|
108
|
-
implemented: true,
|
|
109
|
-
responses: { 200: { code: '' } }
|
|
110
|
-
},
|
|
111
|
-
'GET /SASLogon/logout': {
|
|
112
|
-
implemented: true,
|
|
113
|
-
hostResponses: [500],
|
|
114
|
-
responses: { 200: 'OK!' }
|
|
140
|
+
implemented: false,
|
|
141
|
+
reason: DEPLOY_CAP
|
|
115
142
|
},
|
|
143
|
+
// `404` is a pair - the registry name and the appLoc it was published from -
|
|
144
|
+
// that matches no entry, and the drive's content is deliberately left alone.
|
|
145
|
+
'DELETE /SASjsApi/drive/deploy': {
|
|
146
|
+
implemented: false,
|
|
147
|
+
reason: UNPUBLISH
|
|
148
|
+
},
|
|
149
|
+
// Group - members and the groups themselves
|
|
150
|
+
'GET /SASjsApi/group': { implemented: false, reason: IDENTITY_RECORDS },
|
|
151
|
+
'GET /SASjsApi/group/{uid}': { implemented: false, reason: IDENTITY_RECORDS },
|
|
152
|
+
'GET /SASjsApi/group/by/groupname/{name}': {
|
|
153
|
+
implemented: false,
|
|
154
|
+
reason: IDENTITY_RECORDS
|
|
155
|
+
},
|
|
156
|
+
'POST /SASjsApi/group': { implemented: false, reason: IDENTITY_RECORDS },
|
|
157
|
+
'POST /SASjsApi/group/{groupUid}/{userUid}': {
|
|
158
|
+
implemented: false,
|
|
159
|
+
reason: IDENTITY_RECORDS
|
|
160
|
+
},
|
|
161
|
+
'DELETE /SASjsApi/group/{groupUid}/{userUid}': {
|
|
162
|
+
implemented: false,
|
|
163
|
+
reason: IDENTITY_RECORDS
|
|
164
|
+
},
|
|
165
|
+
'DELETE /SASjsApi/group/{uid}': {
|
|
166
|
+
implemented: false,
|
|
167
|
+
reason: IDENTITY_RECORDS
|
|
168
|
+
},
|
|
169
|
+
// Info - what the host is, and what this caller may use
|
|
170
|
+
'GET /SASjsApi/info': { implemented: false, reason: HOST_CONFIG },
|
|
171
|
+
'GET /SASjsApi/info/authorizedRoutes': {
|
|
172
|
+
implemented: false,
|
|
173
|
+
// the route inventory reports what the CALLER may use, so it needs an
|
|
174
|
+
// identity even though the rest of the Info tag needs nothing
|
|
175
|
+
capabilities: ['identity'],
|
|
176
|
+
reason: PERMISSIONS
|
|
177
|
+
},
|
|
178
|
+
'GET /SASjsApi/info/myAuthorizedRoutes': {
|
|
179
|
+
implemented: false,
|
|
180
|
+
capabilities: ['identity'],
|
|
181
|
+
reason: PERMISSIONS
|
|
182
|
+
},
|
|
183
|
+
// Permission - the rules the inventory above is derived from
|
|
184
|
+
'GET /SASjsApi/permission': { implemented: false, reason: IDENTITY_RECORDS },
|
|
185
|
+
'POST /SASjsApi/permission': { implemented: false, reason: IDENTITY_RECORDS },
|
|
186
|
+
'PATCH /SASjsApi/permission/{uid}': {
|
|
187
|
+
implemented: false,
|
|
188
|
+
reason: IDENTITY_RECORDS
|
|
189
|
+
},
|
|
190
|
+
'DELETE /SASjsApi/permission/{uid}': {
|
|
191
|
+
implemented: false,
|
|
192
|
+
reason: IDENTITY_RECORDS
|
|
193
|
+
},
|
|
194
|
+
// Session - the caller's own session, then the record behind a triggered one
|
|
116
195
|
'GET /SASjsApi/session': {
|
|
117
196
|
implemented: true,
|
|
118
197
|
responses: { 200: user }
|
|
198
|
+
},
|
|
199
|
+
'GET /SASjsApi/session/{sessionId}': {
|
|
200
|
+
implemented: false,
|
|
201
|
+
reason: SESSION_RECORD
|
|
202
|
+
},
|
|
203
|
+
'GET /SASjsApi/session/{sessionId}/log': {
|
|
204
|
+
implemented: false,
|
|
205
|
+
reason: SESSION_RECORD
|
|
206
|
+
},
|
|
207
|
+
'GET /SASjsApi/session/{sessionId}/output': {
|
|
208
|
+
implemented: false,
|
|
209
|
+
reason: SESSION_RECORD
|
|
210
|
+
},
|
|
211
|
+
'GET /SASjsApi/session/{sessionId}/state': {
|
|
212
|
+
implemented: false,
|
|
213
|
+
reason: SESSION_RECORD
|
|
214
|
+
},
|
|
215
|
+
// STP - a stored program, on the runtime port
|
|
216
|
+
'GET /SASjsApi/stp/execute': { implemented: false, reason: RUNTIME },
|
|
217
|
+
'POST /SASjsApi/stp/execute': { implemented: false, reason: RUNTIME },
|
|
218
|
+
'POST /SASjsApi/stp/trigger': { implemented: false, reason: RUNTIME },
|
|
219
|
+
// User - the accounts the identity store already holds for sign-in
|
|
220
|
+
'GET /SASjsApi/user': { implemented: false, reason: IDENTITY_RECORDS },
|
|
221
|
+
'GET /SASjsApi/user/{uid}': { implemented: false, reason: IDENTITY_RECORDS },
|
|
222
|
+
'GET /SASjsApi/user/by/username/{username}': {
|
|
223
|
+
implemented: false,
|
|
224
|
+
reason: IDENTITY_RECORDS
|
|
225
|
+
},
|
|
226
|
+
'POST /SASjsApi/user': { implemented: false, reason: IDENTITY_RECORDS },
|
|
227
|
+
'PATCH /SASjsApi/user/{uid}': {
|
|
228
|
+
implemented: false,
|
|
229
|
+
reason: IDENTITY_RECORDS
|
|
230
|
+
},
|
|
231
|
+
'PATCH /SASjsApi/user/by/username/{username}': {
|
|
232
|
+
implemented: false,
|
|
233
|
+
reason: IDENTITY_RECORDS
|
|
234
|
+
},
|
|
235
|
+
'DELETE /SASjsApi/user/{uid}': {
|
|
236
|
+
implemented: false,
|
|
237
|
+
reason: IDENTITY_RECORDS
|
|
238
|
+
},
|
|
239
|
+
'DELETE /SASjsApi/user/by/username/{username}': {
|
|
240
|
+
implemented: false,
|
|
241
|
+
reason: IDENTITY_RECORDS
|
|
119
242
|
}
|
|
120
243
|
};
|
|
121
244
|
/** Every method the table may state, in the order a diff reads best. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../../src/host/dispatch.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAGnD,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,oFAAoF;IACpF,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAA;IACtD;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,mBAAmB;IACnB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;CAC/B;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,YAAY,CAAA;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAA;CAC3B;AAsCD,eAAO,MAAM,QAAQ,GACnB,UAAU,QAAQ,EAClB,SAAS,MAAM,EACf,UAAS,UAAe,KACvB,OAAO,CAAC,WAAW,
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../../src/host/dispatch.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAGnD,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,oFAAoF;IACpF,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAA;IACtD;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,mBAAmB;IACnB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;CAC/B;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,YAAY,CAAA;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAA;CAC3B;AAsCD,eAAO,MAAM,QAAQ,GACnB,UAAU,QAAQ,EAClB,SAAS,MAAM,EACf,UAAS,UAAe,KACvB,OAAO,CAAC,WAAW,CA8HrB,CAAA;AAED,0DAA0D;AAC1D,eAAO,MAAM,cAAc,EAAE,MAAM,EAiBlC,CAAA"}
|
|
@@ -128,8 +128,13 @@ export const dispatch = async (services, routeId, request = {}) => {
|
|
|
128
128
|
};
|
|
129
129
|
case 'GET /SASjsApi/drive/filetree':
|
|
130
130
|
return { status: 200, body: await drive.tree(query._folderPath) };
|
|
131
|
-
default:
|
|
132
|
-
|
|
131
|
+
default: {
|
|
132
|
+
// The reason the table carries travels with the refusal: a host developer
|
|
133
|
+
// reading a 501 wants to know what the operation waits on, not only that
|
|
134
|
+
// it is missing.
|
|
135
|
+
const reason = routeById(routeId)?.reason;
|
|
136
|
+
throw new ApiError(501, `This package has no handler for ${routeId}.${reason ? ` ${reason}` : ''}`);
|
|
137
|
+
}
|
|
133
138
|
}
|
|
134
139
|
};
|
|
135
140
|
/** Whether a route is one `dispatch` can answer today. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/index.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,KAAK,EAAiB,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/index.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,KAAK,EAAiB,MAAM,aAAa,CAAA;AA4DvD,eAAO,MAAM,MAAM,EAAE,KAAK,EAAgC,CAAA;AAW1D,4EAA4E;AAC5E,eAAO,MAAM,SAAS,GAAI,IAAI,MAAM,KAAG,KAAK,GAAG,SAAyB,CAAA;AAExE,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,KAAK,CAAA;IACZ,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;AAgCD;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,GACpB,QAAQ,MAAM,EACd,MAAM,MAAM,KACX,UAAU,GAAG,SAuBf,CAAA"}
|
|
@@ -10,7 +10,12 @@
|
|
|
10
10
|
import { GENERATED_ROUTES } from './generated.js';
|
|
11
11
|
import { OVERLAY, TAG_CAPABILITIES } from './overlay.js';
|
|
12
12
|
const merge = (generated) => {
|
|
13
|
-
const overlay = OVERLAY[generated.id]
|
|
13
|
+
const overlay = OVERLAY[generated.id];
|
|
14
|
+
// Every operation states its decision. A route with no entry is a route
|
|
15
|
+
// nobody has looked at, which reads exactly like a route that is about to be
|
|
16
|
+
// done - so it fails the build instead, and the message says what to add.
|
|
17
|
+
if (!overlay)
|
|
18
|
+
throw new Error(`src/routes/overlay.ts has no entry for ${generated.id}. Every operation states whether this package implements it and, when it does not, the reason it does not: add \`{ implemented: false, reason: '...' }\` until a handler exists.`);
|
|
14
19
|
const documented = new Map(generated.responses.map((r) => [r.status, r.description]));
|
|
15
20
|
// The document states the statuses and the overlay only annotates the ones a
|
|
16
21
|
// client keys a body on. Naming a status the document does not state is a
|
|
@@ -43,7 +48,8 @@ const merge = (generated) => {
|
|
|
43
48
|
responses,
|
|
44
49
|
hostResponses: overlay.hostResponses || [],
|
|
45
50
|
capabilities: overlay.capabilities || TAG_CAPABILITIES[generated.tag] || [],
|
|
46
|
-
implemented: overlay.implemented
|
|
51
|
+
implemented: overlay.implemented,
|
|
52
|
+
...(overlay.implemented ? {} : { reason: overlay.reason })
|
|
47
53
|
};
|
|
48
54
|
};
|
|
49
55
|
export const ROUTES = GENERATED_ROUTES.map(merge);
|
|
@@ -14,7 +14,16 @@
|
|
|
14
14
|
* server does not send;
|
|
15
15
|
* - which capability the route needs, so a host can answer 501 with a reason
|
|
16
16
|
* instead of 404 with none;
|
|
17
|
-
* - whether this package implements it
|
|
17
|
+
* - whether this package implements it and, when it does not, what the
|
|
18
|
+
* operation is waiting on.
|
|
19
|
+
*
|
|
20
|
+
* EVERY operation in the document has an entry, and the type carries the rule:
|
|
21
|
+
* `implemented: true`, or `implemented: false` with the `reason` it is not. An
|
|
22
|
+
* operation with no entry is a build failure (`src/routes/index.ts`), because
|
|
23
|
+
* silence is indistinguishable from "nobody has looked at this yet" - which is
|
|
24
|
+
* the state this file exists to remove. The reason is also what a 501 carries
|
|
25
|
+
* to whoever asked (`src/host/dispatch.ts`), so the answer names the dependency
|
|
26
|
+
* rather than the absence.
|
|
18
27
|
*
|
|
19
28
|
* The statuses come from the document, and an overlay body is attached to the
|
|
20
29
|
* status it belongs to: naming a status the document does not state is a build
|
|
@@ -23,9 +32,9 @@
|
|
|
23
32
|
* re-vendored here.
|
|
24
33
|
*/
|
|
25
34
|
import type { Capability, HttpMethod } from '../types.js';
|
|
26
|
-
|
|
35
|
+
/** The annotations both states of an entry can carry. */
|
|
36
|
+
interface OverlayFields {
|
|
27
37
|
capabilities?: Capability[];
|
|
28
|
-
implemented?: boolean;
|
|
29
38
|
/**
|
|
30
39
|
* Statuses the document states that this package cannot send because they are
|
|
31
40
|
* the host's to send: an account lockout is the host's login limiter, a
|
|
@@ -41,9 +50,23 @@ export interface RouteOverlay {
|
|
|
41
50
|
*/
|
|
42
51
|
responses?: Record<number, unknown>;
|
|
43
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* One operation's decision: carried here, or carried by the host.
|
|
55
|
+
*
|
|
56
|
+
* A union rather than an optional field, so a route marked unimplemented
|
|
57
|
+
* without saying why does not compile. The reason is the point of the entry -
|
|
58
|
+
* "not done yet" on its own tells a reader nothing they can act on.
|
|
59
|
+
*/
|
|
60
|
+
export type RouteOverlay = OverlayFields & ({
|
|
61
|
+
implemented: true;
|
|
62
|
+
} | {
|
|
63
|
+
implemented: false;
|
|
64
|
+
reason: string;
|
|
65
|
+
});
|
|
44
66
|
/** The default capability for each tag, overridden per route where it differs. */
|
|
45
67
|
export declare const TAG_CAPABILITIES: Record<string, Capability[]>;
|
|
46
68
|
export declare const OVERLAY: Record<string, RouteOverlay>;
|
|
47
69
|
/** Every method the table may state, in the order a diff reads best. */
|
|
48
70
|
export declare const METHODS: HttpMethod[];
|
|
71
|
+
export {};
|
|
49
72
|
//# sourceMappingURL=overlay.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../../src/routes/overlay.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../../src/routes/overlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAEzD,yDAAyD;AACzD,UAAU,aAAa;IACrB,YAAY,CAAC,EAAE,UAAU,EAAE,CAAA;IAC3B;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACpC;AAED;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,aAAa,GACtC,CAAC;IAAE,WAAW,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,WAAW,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAElE,kFAAkF;AAClF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,CAczD,CAAA;AAiDD,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAkNhD,CAAA;AAED,wEAAwE;AACxE,eAAO,MAAM,OAAO,EAAE,UAAU,EAA8C,CAAA"}
|
|
@@ -14,6 +14,23 @@ export const TAG_CAPABILITIES = {
|
|
|
14
14
|
Client: ['identity'],
|
|
15
15
|
Web: ['identity']
|
|
16
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* Why a block of operations is not implemented here.
|
|
19
|
+
*
|
|
20
|
+
* One per dependency rather than one per route: the reason states what the
|
|
21
|
+
* operation waits on, and thirty copies of the same sentence would say the same
|
|
22
|
+
* thing thirty times.
|
|
23
|
+
*/
|
|
24
|
+
const IDENTITY_RECORDS = 'The identity service answers the sign-in flow. User, group, permission rule, client and provider records need read and write methods on the same store, which this package does not have yet.';
|
|
25
|
+
const EDITOR = 'Linting and formatting come from @sasjs/lint, which this package does not depend on. They land with the code routes so an editor has one wiring.';
|
|
26
|
+
const RUNTIME = 'Starting a program needs a runtime port, which this package does not have: a host decides which languages it holds, and answers this itself until then.';
|
|
27
|
+
const SESSION_RECORD = "A triggered session's record, log, output and state belong to the runtime that ran the program, and this package has no runtime port.";
|
|
28
|
+
const HOST_CONFIG = "The body is the host's own configuration - ports, run times and providers - which a mock states for itself and this package cannot know.";
|
|
29
|
+
const PERMISSIONS = "The route inventory is the host's permission model, which this package does not hold.";
|
|
30
|
+
const HOST_OWNED = "The host owns this report: it describes the host's own process and files, which a browser build has no equivalent of.";
|
|
31
|
+
const WEB_BUNDLE = 'The host serves its own web bundle; there is no API decision here for this package to answer.';
|
|
32
|
+
const DEPLOY_CAP = "Writing a whole tree in one request can exhaust the host's storage, so the deploy routes land with the size cap that bounds it.";
|
|
33
|
+
const UNPUBLISH = 'Unpublishing inverts the deploy route, so the two land together.';
|
|
17
34
|
const success = { status: 'success' };
|
|
18
35
|
const user = {
|
|
19
36
|
uid: '',
|
|
@@ -23,11 +40,61 @@ const user = {
|
|
|
23
40
|
needsToUpdatePassword: false
|
|
24
41
|
};
|
|
25
42
|
export const OVERLAY = {
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
'
|
|
29
|
-
|
|
30
|
-
|
|
43
|
+
// Web - the sign-in pages the identity service answers, and the bundle itself
|
|
44
|
+
'GET /': { implemented: false, reason: WEB_BUNDLE },
|
|
45
|
+
'POST /SASLogon/login': {
|
|
46
|
+
implemented: true,
|
|
47
|
+
// The lockout and the provider failure are answered before the package is
|
|
48
|
+
// consulted (routes/web/web.ts in sasjs/server).
|
|
49
|
+
hostResponses: [429, 500],
|
|
50
|
+
responses: { 200: { loggedIn: true, user } }
|
|
51
|
+
},
|
|
52
|
+
'POST /SASLogon/authorize': {
|
|
53
|
+
implemented: true,
|
|
54
|
+
responses: { 200: { code: '' } }
|
|
55
|
+
},
|
|
56
|
+
'GET /SASLogon/logout': {
|
|
57
|
+
implemented: true,
|
|
58
|
+
hostResponses: [500],
|
|
59
|
+
responses: { 200: 'OK!' }
|
|
60
|
+
},
|
|
61
|
+
// Admin - the host's own process and files
|
|
62
|
+
'GET /SASjsApi/admin/diagnostics': { implemented: false, reason: HOST_OWNED },
|
|
63
|
+
'GET /SASjsApi/admin/logs': { implemented: false, reason: HOST_OWNED },
|
|
64
|
+
'GET /SASjsApi/admin/logs/{fileName}': {
|
|
65
|
+
implemented: false,
|
|
66
|
+
reason: HOST_OWNED
|
|
67
|
+
},
|
|
68
|
+
// Auth - the token flow, plus a password change the identity store cannot write
|
|
69
|
+
'POST /SASjsApi/auth/token': {
|
|
70
|
+
implemented: true,
|
|
71
|
+
responses: { 200: { accessToken: '', refreshToken: '' } }
|
|
72
|
+
},
|
|
73
|
+
'POST /SASjsApi/auth/refresh': {
|
|
74
|
+
implemented: true,
|
|
75
|
+
responses: { 200: { accessToken: '', refreshToken: '' } }
|
|
76
|
+
},
|
|
77
|
+
'POST /SASjsApi/auth/logout': { implemented: true },
|
|
78
|
+
'PATCH /SASjsApi/auth/updatePassword': {
|
|
79
|
+
implemented: false,
|
|
80
|
+
reason: IDENTITY_RECORDS
|
|
81
|
+
},
|
|
82
|
+
// Auth Config - the providers a host was started with, and an LDAP import
|
|
83
|
+
'GET /SASjsApi/authConfig': { implemented: false, reason: HOST_CONFIG },
|
|
84
|
+
'POST /SASjsApi/authConfig/synchroniseWithLDAP': {
|
|
85
|
+
implemented: false,
|
|
86
|
+
reason: IDENTITY_RECORDS
|
|
87
|
+
},
|
|
88
|
+
// Client - the OAuth clients the identity store already holds for sign-in
|
|
89
|
+
'GET /SASjsApi/client': { implemented: false, reason: IDENTITY_RECORDS },
|
|
90
|
+
'POST /SASjsApi/client': { implemented: false, reason: IDENTITY_RECORDS },
|
|
91
|
+
// Code - the editor's three calls, and the runtime one
|
|
92
|
+
'POST /SASjsApi/code/execute': { implemented: false, reason: RUNTIME },
|
|
93
|
+
'POST /SASjsApi/code/trigger': { implemented: false, reason: RUNTIME },
|
|
94
|
+
'POST /SASjsApi/code/lint': { implemented: false, reason: EDITOR },
|
|
95
|
+
'POST /SASjsApi/code/format': { implemented: false, reason: EDITOR },
|
|
96
|
+
// Drive - the file and folder rules, and the three deploy routes
|
|
97
|
+
// The body is the file itself, so there is nothing to annotate
|
|
31
98
|
'GET /SASjsApi/drive/file': { implemented: true },
|
|
32
99
|
'POST /SASjsApi/drive/file': {
|
|
33
100
|
implemented: true,
|
|
@@ -65,54 +132,110 @@ export const OVERLAY = {
|
|
|
65
132
|
implemented: true,
|
|
66
133
|
responses: { 200: { status: 'success', tree: {} } }
|
|
67
134
|
},
|
|
68
|
-
|
|
69
|
-
// ones where a caller can exhaust the host's storage, so they carry a cap -
|
|
70
|
-
// see the plan in sasjs/cli#1550 - and they are not implemented yet.
|
|
71
|
-
'POST /SASjsApi/drive/deploy': {
|
|
72
|
-
implemented: false
|
|
73
|
-
},
|
|
135
|
+
'POST /SASjsApi/drive/deploy': { implemented: false, reason: DEPLOY_CAP },
|
|
74
136
|
'POST /SASjsApi/drive/deploy/upload': {
|
|
75
|
-
implemented: false
|
|
76
|
-
|
|
77
|
-
// Unpublishing is the counterpart of the two above and lands with them: the
|
|
78
|
-
// document in sasjs/server states it (sasjs/server#543), so a host may answer
|
|
79
|
-
// it in the meantime, but this package cannot until the publish side it would
|
|
80
|
-
// invert is implemented here. The statuses are the document's; `404` is a pair
|
|
81
|
-
// - the registry name and the appLoc it was published from - that matches no
|
|
82
|
-
// entry, and the drive's content is deliberately left alone.
|
|
83
|
-
'DELETE /SASjsApi/drive/deploy': {
|
|
84
|
-
implemented: false
|
|
85
|
-
},
|
|
86
|
-
// The lockout and the provider failure are answered before the package is
|
|
87
|
-
// consulted (routes/web/web.ts in sasjs/server).
|
|
88
|
-
'POST /SASLogon/login': {
|
|
89
|
-
implemented: true,
|
|
90
|
-
hostResponses: [429, 500],
|
|
91
|
-
responses: { 200: { loggedIn: true, user } }
|
|
92
|
-
},
|
|
93
|
-
// The sign-in flow. The statuses are the document's; these entries state the
|
|
94
|
-
// body a client keys on, and that this package answers the operation.
|
|
95
|
-
'POST /SASjsApi/auth/token': {
|
|
96
|
-
implemented: true,
|
|
97
|
-
responses: { 200: { accessToken: '', refreshToken: '' } }
|
|
98
|
-
},
|
|
99
|
-
'POST /SASjsApi/auth/refresh': {
|
|
100
|
-
implemented: true,
|
|
101
|
-
responses: { 200: { accessToken: '', refreshToken: '' } }
|
|
102
|
-
},
|
|
103
|
-
'POST /SASjsApi/auth/logout': { implemented: true },
|
|
104
|
-
'POST /SASLogon/authorize': {
|
|
105
|
-
implemented: true,
|
|
106
|
-
responses: { 200: { code: '' } }
|
|
107
|
-
},
|
|
108
|
-
'GET /SASLogon/logout': {
|
|
109
|
-
implemented: true,
|
|
110
|
-
hostResponses: [500],
|
|
111
|
-
responses: { 200: 'OK!' }
|
|
137
|
+
implemented: false,
|
|
138
|
+
reason: DEPLOY_CAP
|
|
112
139
|
},
|
|
140
|
+
// `404` is a pair - the registry name and the appLoc it was published from -
|
|
141
|
+
// that matches no entry, and the drive's content is deliberately left alone.
|
|
142
|
+
'DELETE /SASjsApi/drive/deploy': {
|
|
143
|
+
implemented: false,
|
|
144
|
+
reason: UNPUBLISH
|
|
145
|
+
},
|
|
146
|
+
// Group - members and the groups themselves
|
|
147
|
+
'GET /SASjsApi/group': { implemented: false, reason: IDENTITY_RECORDS },
|
|
148
|
+
'GET /SASjsApi/group/{uid}': { implemented: false, reason: IDENTITY_RECORDS },
|
|
149
|
+
'GET /SASjsApi/group/by/groupname/{name}': {
|
|
150
|
+
implemented: false,
|
|
151
|
+
reason: IDENTITY_RECORDS
|
|
152
|
+
},
|
|
153
|
+
'POST /SASjsApi/group': { implemented: false, reason: IDENTITY_RECORDS },
|
|
154
|
+
'POST /SASjsApi/group/{groupUid}/{userUid}': {
|
|
155
|
+
implemented: false,
|
|
156
|
+
reason: IDENTITY_RECORDS
|
|
157
|
+
},
|
|
158
|
+
'DELETE /SASjsApi/group/{groupUid}/{userUid}': {
|
|
159
|
+
implemented: false,
|
|
160
|
+
reason: IDENTITY_RECORDS
|
|
161
|
+
},
|
|
162
|
+
'DELETE /SASjsApi/group/{uid}': {
|
|
163
|
+
implemented: false,
|
|
164
|
+
reason: IDENTITY_RECORDS
|
|
165
|
+
},
|
|
166
|
+
// Info - what the host is, and what this caller may use
|
|
167
|
+
'GET /SASjsApi/info': { implemented: false, reason: HOST_CONFIG },
|
|
168
|
+
'GET /SASjsApi/info/authorizedRoutes': {
|
|
169
|
+
implemented: false,
|
|
170
|
+
// the route inventory reports what the CALLER may use, so it needs an
|
|
171
|
+
// identity even though the rest of the Info tag needs nothing
|
|
172
|
+
capabilities: ['identity'],
|
|
173
|
+
reason: PERMISSIONS
|
|
174
|
+
},
|
|
175
|
+
'GET /SASjsApi/info/myAuthorizedRoutes': {
|
|
176
|
+
implemented: false,
|
|
177
|
+
capabilities: ['identity'],
|
|
178
|
+
reason: PERMISSIONS
|
|
179
|
+
},
|
|
180
|
+
// Permission - the rules the inventory above is derived from
|
|
181
|
+
'GET /SASjsApi/permission': { implemented: false, reason: IDENTITY_RECORDS },
|
|
182
|
+
'POST /SASjsApi/permission': { implemented: false, reason: IDENTITY_RECORDS },
|
|
183
|
+
'PATCH /SASjsApi/permission/{uid}': {
|
|
184
|
+
implemented: false,
|
|
185
|
+
reason: IDENTITY_RECORDS
|
|
186
|
+
},
|
|
187
|
+
'DELETE /SASjsApi/permission/{uid}': {
|
|
188
|
+
implemented: false,
|
|
189
|
+
reason: IDENTITY_RECORDS
|
|
190
|
+
},
|
|
191
|
+
// Session - the caller's own session, then the record behind a triggered one
|
|
113
192
|
'GET /SASjsApi/session': {
|
|
114
193
|
implemented: true,
|
|
115
194
|
responses: { 200: user }
|
|
195
|
+
},
|
|
196
|
+
'GET /SASjsApi/session/{sessionId}': {
|
|
197
|
+
implemented: false,
|
|
198
|
+
reason: SESSION_RECORD
|
|
199
|
+
},
|
|
200
|
+
'GET /SASjsApi/session/{sessionId}/log': {
|
|
201
|
+
implemented: false,
|
|
202
|
+
reason: SESSION_RECORD
|
|
203
|
+
},
|
|
204
|
+
'GET /SASjsApi/session/{sessionId}/output': {
|
|
205
|
+
implemented: false,
|
|
206
|
+
reason: SESSION_RECORD
|
|
207
|
+
},
|
|
208
|
+
'GET /SASjsApi/session/{sessionId}/state': {
|
|
209
|
+
implemented: false,
|
|
210
|
+
reason: SESSION_RECORD
|
|
211
|
+
},
|
|
212
|
+
// STP - a stored program, on the runtime port
|
|
213
|
+
'GET /SASjsApi/stp/execute': { implemented: false, reason: RUNTIME },
|
|
214
|
+
'POST /SASjsApi/stp/execute': { implemented: false, reason: RUNTIME },
|
|
215
|
+
'POST /SASjsApi/stp/trigger': { implemented: false, reason: RUNTIME },
|
|
216
|
+
// User - the accounts the identity store already holds for sign-in
|
|
217
|
+
'GET /SASjsApi/user': { implemented: false, reason: IDENTITY_RECORDS },
|
|
218
|
+
'GET /SASjsApi/user/{uid}': { implemented: false, reason: IDENTITY_RECORDS },
|
|
219
|
+
'GET /SASjsApi/user/by/username/{username}': {
|
|
220
|
+
implemented: false,
|
|
221
|
+
reason: IDENTITY_RECORDS
|
|
222
|
+
},
|
|
223
|
+
'POST /SASjsApi/user': { implemented: false, reason: IDENTITY_RECORDS },
|
|
224
|
+
'PATCH /SASjsApi/user/{uid}': {
|
|
225
|
+
implemented: false,
|
|
226
|
+
reason: IDENTITY_RECORDS
|
|
227
|
+
},
|
|
228
|
+
'PATCH /SASjsApi/user/by/username/{username}': {
|
|
229
|
+
implemented: false,
|
|
230
|
+
reason: IDENTITY_RECORDS
|
|
231
|
+
},
|
|
232
|
+
'DELETE /SASjsApi/user/{uid}': {
|
|
233
|
+
implemented: false,
|
|
234
|
+
reason: IDENTITY_RECORDS
|
|
235
|
+
},
|
|
236
|
+
'DELETE /SASjsApi/user/by/username/{username}': {
|
|
237
|
+
implemented: false,
|
|
238
|
+
reason: IDENTITY_RECORDS
|
|
116
239
|
}
|
|
117
240
|
};
|
|
118
241
|
/** Every method the table may state, in the order a diff reads best. */
|
package/build/esm/types.d.ts
CHANGED
|
@@ -85,6 +85,13 @@ export interface Route extends GeneratedRoute {
|
|
|
85
85
|
* not an absence nobody can see.
|
|
86
86
|
*/
|
|
87
87
|
implemented: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Why this package does not implement the operation, and what it waits on.
|
|
90
|
+
* Present exactly when `implemented` is false: the overlay's entries are a
|
|
91
|
+
* decision either way, and `src/routes/index.ts` refuses to build a table with
|
|
92
|
+
* an operation nobody has decided about. A 501 carries it to the caller.
|
|
93
|
+
*/
|
|
94
|
+
reason?: string;
|
|
88
95
|
}
|
|
89
96
|
/**
|
|
90
97
|
* The drive, as the services see it.
|
package/build/esm/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAK/B,QAAQ,CAAC,MAAM,EAAE,MAAM;IAJzB,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,QAAO;gBAGb,MAAM,EAAE,MAAM,EACvB,OAAO,EAAE,MAAM;CAKlB;AAED;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,QACH,CAAA;AAElD;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GAClB,SAAS,GACT,SAAS,GACT,UAAU,GACV,KAAK,GACL,MAAM,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAA;AAEpE,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAA;AAE7E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,aAAa,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,qEAAqE;IACrE,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,8DAA8D;AAC9D,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,UAAU,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,UAAU,EAAE,CAAA;IACpB,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,SAAS,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC1D;AAED,MAAM,WAAW,KAAM,SAAQ,cAAc;IAC3C,SAAS,EAAE,aAAa,EAAE,CAAA;IAC1B;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,YAAY,EAAE,UAAU,EAAE,CAAA;IAC1B;;;;OAIG;IACH,WAAW,EAAE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAK/B,QAAQ,CAAC,MAAM,EAAE,MAAM;IAJzB,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,QAAO;gBAGb,MAAM,EAAE,MAAM,EACvB,OAAO,EAAE,MAAM;CAKlB;AAED;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,QACH,CAAA;AAElD;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GAClB,SAAS,GACT,SAAS,GACT,UAAU,GACV,KAAK,GACL,MAAM,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAA;AAEpE,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAA;AAE7E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,aAAa,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,qEAAqE;IACrE,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,8DAA8D;AAC9D,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,UAAU,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,UAAU,EAAE,CAAA;IACpB,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,SAAS,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC1D;AAED,MAAM,WAAW,KAAM,SAAQ,cAAc;IAC3C,SAAS,EAAE,aAAa,EAAE,CAAA;IAC1B;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,YAAY,EAAE,UAAU,EAAE,CAAA;IAC1B;;;;OAIG;IACH,WAAW,EAAE,OAAO,CAAA;IACpB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,UAAU,CAAC,CAAA;IAChD,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACtC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACjE,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAA;IACnE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,4DAA4D;IAC5D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9C;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,QAAQ,EAAE,CAAA;CACrB"}
|
package/package.json
CHANGED