@oxy.so/core 7.18.0 → 7.19.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/cjs/HttpService.js +2 -0
- package/dist/cjs/OxyServices.errors.js +21 -0
- package/dist/cjs/OxyServices.js +86 -75
- package/dist/cjs/client/callOptions.js +66 -0
- package/dist/cjs/index.js +5 -4
- package/dist/cjs/server/OxyServer.js +10 -27
- package/dist/cjs/server/namespaces.js +85 -26
- package/dist/cjs/server/requesterClient.js +1 -3
- package/dist/cjs/server/safeFetch.js +43 -12
- package/dist/esm/HttpService.js +2 -1
- package/dist/esm/OxyServices.errors.js +20 -0
- package/dist/esm/OxyServices.js +86 -75
- package/dist/esm/client/callOptions.js +61 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/server/OxyServer.js +10 -27
- package/dist/esm/server/namespaces.js +86 -27
- package/dist/esm/server/requesterClient.js +1 -3
- package/dist/esm/server/safeFetch.js +43 -12
- package/dist/types/HttpService.d.ts +2 -0
- package/dist/types/OxyServices.d.ts +38 -25
- package/dist/types/OxyServices.errors.d.ts +6 -0
- package/dist/types/client/callOptions.d.ts +45 -0
- package/dist/types/index.d.ts +4 -3
- package/dist/types/models/interfaces.d.ts +23 -0
- package/dist/types/server/OxyServer.d.ts +0 -9
- package/dist/types/server/namespaces.d.ts +50 -13
- package/dist/types/server/requesterClient.d.ts +0 -1
- package/dist/types/server/safeFetch.d.ts +16 -0
- package/package.json +1 -1
- package/src/HttpService.ts +2 -1
- package/src/OxyServices.errors.ts +19 -0
- package/src/OxyServices.ts +129 -75
- package/src/client/callOptions.ts +88 -0
- package/src/index.ts +12 -1
- package/src/models/interfaces.ts +25 -0
- package/src/server/OxyServer.ts +15 -33
- package/src/server/namespaces.ts +128 -43
- package/src/server/requesterClient.ts +1 -3
- package/src/server/safeFetch.ts +69 -11
package/dist/cjs/HttpService.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
exports.HttpService = void 0;
|
|
18
|
+
exports.linkAbort = linkAbort;
|
|
18
19
|
const cache_1 = require("./utils/cache");
|
|
19
20
|
const requestUtils_1 = require("./utils/requestUtils");
|
|
20
21
|
const asyncUtils_1 = require("./utils/asyncUtils");
|
|
@@ -301,6 +302,7 @@ function isAbortLike(error) {
|
|
|
301
302
|
error !== null &&
|
|
302
303
|
error.name === 'AbortError');
|
|
303
304
|
}
|
|
305
|
+
/** @internal Shared with `client/callOptions.ts`; see the note above `isAbortLike`. */
|
|
304
306
|
function linkAbort(parent, child) {
|
|
305
307
|
if (parent.aborted) {
|
|
306
308
|
child.abort(parent.reason);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OxyApiError = exports.OxyAuthenticationTimeoutError = exports.ServiceLinkedDownloadUrlError = exports.ServiceAssetMetadataError = exports.AssetUrlResolutionError = exports.OxyAuthenticationError = void 0;
|
|
4
|
+
exports.retryAfterSecondsOf = retryAfterSecondsOf;
|
|
4
5
|
exports.toOxyApiError = toOxyApiError;
|
|
5
6
|
const errorUtils_1 = require("./utils/errorUtils");
|
|
6
7
|
/**
|
|
@@ -146,6 +147,26 @@ class OxyApiError extends Error {
|
|
|
146
147
|
}
|
|
147
148
|
}
|
|
148
149
|
exports.OxyApiError = OxyApiError;
|
|
150
|
+
/**
|
|
151
|
+
* How long Oxy asked the caller to wait before trying again, in seconds — the
|
|
152
|
+
* `Retry-After` of a 429 (`details.retryAfterSeconds` of its body) — or
|
|
153
|
+
* `undefined` when the error is no such refusal or names no wait.
|
|
154
|
+
*/
|
|
155
|
+
function retryAfterSecondsOf(error) {
|
|
156
|
+
if (!error || typeof error !== 'object')
|
|
157
|
+
return undefined;
|
|
158
|
+
const candidates = [
|
|
159
|
+
error.details,
|
|
160
|
+
error.response
|
|
161
|
+
?.data?.details,
|
|
162
|
+
];
|
|
163
|
+
for (const details of candidates) {
|
|
164
|
+
const seconds = details?.retryAfterSeconds;
|
|
165
|
+
if (typeof seconds === 'number' && Number.isFinite(seconds) && seconds > 0)
|
|
166
|
+
return seconds;
|
|
167
|
+
}
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
149
170
|
/**
|
|
150
171
|
* Normalise anything a request rejected with into an `OxyApiError`. An error
|
|
151
172
|
* that already is one (or a typed SDK error such as `OxyAuthenticationError`)
|
package/dist/cjs/OxyServices.js
CHANGED
|
@@ -28,6 +28,7 @@ const contentClaims_1 = require("./api/contentClaims");
|
|
|
28
28
|
*/
|
|
29
29
|
const HttpService_1 = require("./HttpService");
|
|
30
30
|
const OxyServices_errors_1 = require("./OxyServices.errors");
|
|
31
|
+
const callOptions_1 = require("./client/callOptions");
|
|
31
32
|
const session_1 = require("./api/session");
|
|
32
33
|
const auth_1 = require("./api/auth");
|
|
33
34
|
const users_1 = require("./api/users");
|
|
@@ -67,6 +68,9 @@ class OxyServices {
|
|
|
67
68
|
deletePrefix: (prefix) => this.http.clearCacheByPrefix(prefix),
|
|
68
69
|
stats: () => this.http.getCacheStats(),
|
|
69
70
|
};
|
|
71
|
+
// ── Namespaces (created on first access) ─────────────────────────────────
|
|
72
|
+
/** Each namespace, created on first access and kept for the client's life. */
|
|
73
|
+
this.namespaceInstances = new Map();
|
|
70
74
|
if (!config || typeof config !== 'object') {
|
|
71
75
|
throw new Error('OxyConfig is required');
|
|
72
76
|
}
|
|
@@ -169,151 +173,158 @@ class OxyServices {
|
|
|
169
173
|
dispose() {
|
|
170
174
|
this.http.dispose();
|
|
171
175
|
}
|
|
176
|
+
/** The namespace `name`, created with this client's context on first access. */
|
|
177
|
+
namespace(name, create) {
|
|
178
|
+
let instance = this.namespaceInstances.get(name);
|
|
179
|
+
if (instance === undefined) {
|
|
180
|
+
instance = create(this.context);
|
|
181
|
+
this.namespaceInstances.set(name, instance);
|
|
182
|
+
}
|
|
183
|
+
return instance;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* This client's namespaces with per-call transport options: every request
|
|
187
|
+
* made through them carries `options`, on top of what each method sets.
|
|
188
|
+
*
|
|
189
|
+
* ```ts
|
|
190
|
+
* // One slow write, one tight read — one client.
|
|
191
|
+
* await oxy.withCallOptions({ timeout: 60_000 }).assets.setVisibility(id, 'public');
|
|
192
|
+
* const page = await oxy.withCallOptions({ deadline: 2_500 }).users.byUsername(name);
|
|
193
|
+
* // A server's own namespaces too:
|
|
194
|
+
* await server.withCallOptions({ retry: false }).assets.references.add(refs);
|
|
195
|
+
* ```
|
|
196
|
+
*
|
|
197
|
+
* - `timeout` / `deadline` replace the method's own and the client's.
|
|
198
|
+
* - `retry: false` sends each request once; `retry: true` never re-enables
|
|
199
|
+
* a request the method sends once on purpose.
|
|
200
|
+
* - `signal` aborts the call; a method's own signal still applies too.
|
|
201
|
+
*
|
|
202
|
+
* The scope is a view, not a client: it shares this client's session,
|
|
203
|
+
* cache, credentials and service lane, holds no resource and needs no
|
|
204
|
+
* disposal. A namespace one call makes into another (`privacy` reading
|
|
205
|
+
* `users.me()`) keeps the options. `session` is not part of it.
|
|
206
|
+
*/
|
|
207
|
+
withCallOptions(options) {
|
|
208
|
+
(0, callOptions_1.assertCallOptions)(options);
|
|
209
|
+
const scope = { ...options };
|
|
210
|
+
const base = this.context;
|
|
211
|
+
const lane = base.service;
|
|
212
|
+
const view = Object.create(this);
|
|
213
|
+
const context = {
|
|
214
|
+
oxy: view,
|
|
215
|
+
http: base.http,
|
|
216
|
+
request: (method, url, data, requestOptions) => (0, callOptions_1.sendWithCallOptions)(requestOptions, scope, (merged) => base.request(method, url, data, merged)),
|
|
217
|
+
service: lane && {
|
|
218
|
+
get available() {
|
|
219
|
+
return lane.available;
|
|
220
|
+
},
|
|
221
|
+
request: (method, url, data, requestOptions) => (0, callOptions_1.sendWithCallOptions)(requestOptions, scope, (merged) => lane.request(method, url, data, merged)),
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
// The view's own namespaces are built on the scoped context; everything
|
|
225
|
+
// else (http, cache, config, credentials) is this client's, by prototype.
|
|
226
|
+
Object.defineProperties(view, {
|
|
227
|
+
context: { value: context },
|
|
228
|
+
namespaceInstances: { value: new Map() },
|
|
229
|
+
session: { get: () => this.session },
|
|
230
|
+
});
|
|
231
|
+
return view;
|
|
232
|
+
}
|
|
172
233
|
/** This client's auth state (token, user id) and its server session. */
|
|
173
234
|
get session() {
|
|
174
|
-
|
|
175
|
-
this._session = new session_1.SessionApi(this.context);
|
|
176
|
-
return this._session;
|
|
235
|
+
return this.namespace('session', (context) => new session_1.SessionApi(context));
|
|
177
236
|
}
|
|
178
237
|
/** Signing in, signing up, and the account's credentials. */
|
|
179
238
|
get auth() {
|
|
180
|
-
|
|
181
|
-
this._auth = new auth_1.AuthApi(this.context);
|
|
182
|
-
return this._auth;
|
|
239
|
+
return this.namespace('auth', (context) => new auth_1.AuthApi(context));
|
|
183
240
|
}
|
|
184
241
|
/** People and their profiles. */
|
|
185
242
|
get users() {
|
|
186
|
-
|
|
187
|
-
this._users = new users_1.UsersApi(this.context);
|
|
188
|
-
return this._users;
|
|
243
|
+
return this.namespace('users', (context) => new users_1.UsersApi(context));
|
|
189
244
|
}
|
|
190
245
|
/** The follow graph: people and every other followable target. */
|
|
191
246
|
get follows() {
|
|
192
|
-
|
|
193
|
-
this._follows = new follows_1.FollowsApi(this.context);
|
|
194
|
-
return this._follows;
|
|
247
|
+
return this.namespace('follows', (context) => new follows_1.FollowsApi(context));
|
|
195
248
|
}
|
|
196
249
|
/** Privacy settings, blocks and restrictions. */
|
|
197
250
|
get privacy() {
|
|
198
|
-
|
|
199
|
-
this._privacy = new privacy_1.PrivacyApi(this.context);
|
|
200
|
-
return this._privacy;
|
|
251
|
+
return this.namespace('privacy', (context) => new privacy_1.PrivacyApi(context));
|
|
201
252
|
}
|
|
202
253
|
/** The in-app inbox and push tokens. */
|
|
203
254
|
get notifications() {
|
|
204
|
-
|
|
205
|
-
this._notifications = new notifications_1.NotificationsApi(this.context);
|
|
206
|
-
return this._notifications;
|
|
255
|
+
return this.namespace('notifications', (context) => new notifications_1.NotificationsApi(context));
|
|
207
256
|
}
|
|
208
257
|
/** Files: upload, link, URLs, content. */
|
|
209
258
|
get assets() {
|
|
210
|
-
|
|
211
|
-
this._assets = new assets_1.AssetsApi(this.context);
|
|
212
|
-
return this._assets;
|
|
259
|
+
return this.namespace('assets', (context) => new assets_1.AssetsApi(context));
|
|
213
260
|
}
|
|
214
261
|
/** DID, keys, identity links, domains, backup and the signed export. */
|
|
215
262
|
get identity() {
|
|
216
|
-
|
|
217
|
-
this._identity = new identity_1.IdentityApi(this.context);
|
|
218
|
-
return this._identity;
|
|
263
|
+
return this.namespace('identity', (context) => new identity_1.IdentityApi(context));
|
|
219
264
|
}
|
|
220
265
|
/** The account graph: accounts and their members. */
|
|
221
266
|
get safety() {
|
|
222
|
-
|
|
223
|
-
this._safety = new safety_1.SafetyApi(this.context);
|
|
224
|
-
return this._safety;
|
|
267
|
+
return this.namespace('safety', (context) => new safety_1.SafetyApi(context));
|
|
225
268
|
}
|
|
226
269
|
/** Content claims: an owner's counter-notice to a takedown (ADR 0043 D10). */
|
|
227
270
|
get contentClaims() {
|
|
228
|
-
|
|
229
|
-
this._contentClaims = new contentClaims_1.ContentClaimsApi(this.context);
|
|
230
|
-
return this._contentClaims;
|
|
271
|
+
return this.namespace('contentClaims', (context) => new contentClaims_1.ContentClaimsApi(context));
|
|
231
272
|
}
|
|
232
273
|
get organizations() {
|
|
233
|
-
|
|
234
|
-
this._organizations = new organizations_1.OrganizationsApi(this.context);
|
|
235
|
-
return this._organizations;
|
|
274
|
+
return this.namespace('organizations', (context) => new organizations_1.OrganizationsApi(context));
|
|
236
275
|
}
|
|
237
276
|
get accounts() {
|
|
238
|
-
|
|
239
|
-
this._accounts = new accounts_1.AccountsApi(this.context);
|
|
240
|
-
return this._accounts;
|
|
277
|
+
return this.namespace('accounts', (context) => new accounts_1.AccountsApi(context));
|
|
241
278
|
}
|
|
242
279
|
/** Applications an account owns, and the ones this user granted access. */
|
|
243
280
|
get apps() {
|
|
244
|
-
|
|
245
|
-
this._apps = new apps_1.AppsApi(this.context);
|
|
246
|
-
return this._apps;
|
|
281
|
+
return this.namespace('apps', (context) => new apps_1.AppsApi(context));
|
|
247
282
|
}
|
|
248
283
|
/** External accounts (Mastodon, Bluesky) linked by OAuth. */
|
|
249
284
|
get linkedAccounts() {
|
|
250
|
-
|
|
251
|
-
this._linkedAccounts = new linkedAccounts_1.LinkedAccountsApi(this.context);
|
|
252
|
-
return this._linkedAccounts;
|
|
285
|
+
return this.namespace('linkedAccounts', (context) => new linkedAccounts_1.LinkedAccountsApi(context));
|
|
253
286
|
}
|
|
254
287
|
/** Delegated capabilities for agents. */
|
|
255
288
|
get agency() {
|
|
256
|
-
|
|
257
|
-
this._agency = new agency_1.AgencyApi(this.context);
|
|
258
|
-
return this._agency;
|
|
289
|
+
return this.namespace('agency', (context) => new agency_1.AgencyApi(context));
|
|
259
290
|
}
|
|
260
291
|
/** The app store: storefront, reviews, and a publisher's listing. */
|
|
261
292
|
get store() {
|
|
262
|
-
|
|
263
|
-
this._store = new store_1.StoreApi(this.context);
|
|
264
|
-
return this._store;
|
|
293
|
+
return this.namespace('store', (context) => new store_1.StoreApi(context));
|
|
265
294
|
}
|
|
266
295
|
/** Subscription, wallet and payments. */
|
|
267
296
|
get billing() {
|
|
268
|
-
|
|
269
|
-
this._billing = new billing_1.BillingApi(this.context);
|
|
270
|
-
return this._billing;
|
|
297
|
+
return this.namespace('billing', (context) => new billing_1.BillingApi(context));
|
|
271
298
|
}
|
|
272
299
|
/** Oxy Trust reputation (read-only for people; apps award it). */
|
|
273
300
|
get reputation() {
|
|
274
|
-
|
|
275
|
-
this._reputation = new reputation_1.ReputationApi(this.context);
|
|
276
|
-
return this._reputation;
|
|
301
|
+
return this.namespace('reputation', (context) => new reputation_1.ReputationApi(context));
|
|
277
302
|
}
|
|
278
303
|
/** Oxy ID: cards, attestations, vouching, personhood, credentials. */
|
|
279
304
|
get civic() {
|
|
280
|
-
|
|
281
|
-
this._civic = new civic_1.CivicApi(this.context);
|
|
282
|
-
return this._civic;
|
|
305
|
+
return this.namespace('civic', (context) => new civic_1.CivicApi(context));
|
|
283
306
|
}
|
|
284
307
|
/** The user's personal data node. */
|
|
285
308
|
get nodes() {
|
|
286
|
-
|
|
287
|
-
this._nodes = new nodes_1.NodesApi(this.context);
|
|
288
|
-
return this._nodes;
|
|
309
|
+
return this.namespace('nodes', (context) => new nodes_1.NodesApi(context));
|
|
289
310
|
}
|
|
290
311
|
/** This user's devices, their sessions, and device sign-in. */
|
|
291
312
|
get devices() {
|
|
292
|
-
|
|
293
|
-
this._devices = new devices_1.DevicesApi(this.context);
|
|
294
|
-
return this._devices;
|
|
313
|
+
return this.namespace('devices', (context) => new devices_1.DevicesApi(context));
|
|
295
314
|
}
|
|
296
315
|
get topics() {
|
|
297
|
-
|
|
298
|
-
this._topics = new topics_1.TopicsApi(this.context);
|
|
299
|
-
return this._topics;
|
|
316
|
+
return this.namespace('topics', (context) => new topics_1.TopicsApi(context));
|
|
300
317
|
}
|
|
301
318
|
/** Per-app key/value data stored on the user's account. */
|
|
302
319
|
get appData() {
|
|
303
|
-
|
|
304
|
-
this._appData = new appData_1.AppDataApi(this.context);
|
|
305
|
-
return this._appData;
|
|
320
|
+
return this.namespace('appData', (context) => new appData_1.AppDataApi(context));
|
|
306
321
|
}
|
|
307
322
|
/** Oxy Console: first-run onboarding (terms and use cases) for the signed-in person. */
|
|
308
323
|
get console() {
|
|
309
|
-
|
|
310
|
-
this._console = new console_1.ConsoleApi(this.context);
|
|
311
|
-
return this._console;
|
|
324
|
+
return this.namespace('console', (context) => new console_1.ConsoleApi(context));
|
|
312
325
|
}
|
|
313
326
|
get contacts() {
|
|
314
|
-
|
|
315
|
-
this._contacts = new contacts_1.ContactsApi(this.context);
|
|
316
|
-
return this._contacts;
|
|
327
|
+
return this.namespace('contacts', (context) => new contacts_1.ContactsApi(context));
|
|
317
328
|
}
|
|
318
329
|
}
|
|
319
330
|
exports.OxyServices = OxyServices;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assertCallOptions = assertCallOptions;
|
|
4
|
+
exports.applyCallOptions = applyCallOptions;
|
|
5
|
+
exports.sendWithCallOptions = sendWithCallOptions;
|
|
6
|
+
/**
|
|
7
|
+
* Per-call transport options, for any namespace call.
|
|
8
|
+
*
|
|
9
|
+
* `oxy.withCallOptions({ timeout: 60_000 }).assets.setVisibility(id, 'public')`
|
|
10
|
+
* sends that one call with its own budget, without a second client and without
|
|
11
|
+
* every method growing an options parameter of its own. The scope is a view of
|
|
12
|
+
* the same client: same session, cache, service lane and namespaces' state.
|
|
13
|
+
*/
|
|
14
|
+
const HttpService_1 = require("../HttpService");
|
|
15
|
+
/** Throws for a budget the transport cannot honour (0, negative, NaN, Infinity). */
|
|
16
|
+
function assertCallOptions(options) {
|
|
17
|
+
for (const key of ['timeout', 'deadline']) {
|
|
18
|
+
const value = options[key];
|
|
19
|
+
if (value !== undefined && (!Number.isFinite(value) || value <= 0)) {
|
|
20
|
+
throw new RangeError(`${key} must be a positive number of milliseconds`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The request options a method chose, with the scope's applied on top. The
|
|
26
|
+
* signal is NOT merged here: two signals need a linked controller that lives
|
|
27
|
+
* exactly as long as the request ({@link sendWithCallOptions}).
|
|
28
|
+
*/
|
|
29
|
+
function applyCallOptions(method, scope) {
|
|
30
|
+
const merged = { ...method };
|
|
31
|
+
if (scope.timeout !== undefined)
|
|
32
|
+
merged.timeout = scope.timeout;
|
|
33
|
+
if (scope.deadline !== undefined)
|
|
34
|
+
merged.deadline = scope.deadline;
|
|
35
|
+
if (scope.retry === false)
|
|
36
|
+
merged.retry = false;
|
|
37
|
+
else if (scope.retry === true && merged.retry === undefined)
|
|
38
|
+
merged.retry = true;
|
|
39
|
+
return merged;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Send one request with the scope applied: `send` receives the merged
|
|
43
|
+
* options, with a signal that aborts when either the method's or the scope's
|
|
44
|
+
* does. The link between them is released when the request settles.
|
|
45
|
+
*/
|
|
46
|
+
async function sendWithCallOptions(method, scope, send) {
|
|
47
|
+
const merged = applyCallOptions(method, scope);
|
|
48
|
+
const own = method?.signal;
|
|
49
|
+
if (!scope.signal || !own || own === scope.signal) {
|
|
50
|
+
const signal = scope.signal ?? own;
|
|
51
|
+
if (signal)
|
|
52
|
+
merged.signal = signal;
|
|
53
|
+
return send(merged);
|
|
54
|
+
}
|
|
55
|
+
const controller = new AbortController();
|
|
56
|
+
const unlinkOwn = (0, HttpService_1.linkAbort)(own, controller);
|
|
57
|
+
const unlinkScope = (0, HttpService_1.linkAbort)(scope.signal, controller);
|
|
58
|
+
merged.signal = controller.signal;
|
|
59
|
+
try {
|
|
60
|
+
return await send(merged);
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
unlinkOwn();
|
|
64
|
+
unlinkScope();
|
|
65
|
+
}
|
|
66
|
+
}
|
package/dist/cjs/index.js
CHANGED
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
* If a symbol does not appear here, it is NOT part of the public API.
|
|
22
22
|
*/
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
-
exports.
|
|
25
|
-
exports.
|
|
26
|
-
exports.
|
|
27
|
-
exports.packageInfo = exports.consumeOAuthReturnPath = exports.persistOAuthReturnPath = exports.clearOAuthHandshake = exports.readOAuthHandshake = exports.persistOAuthHandshake = exports.canonicalizeOAuthRedirectUri = exports.normalizeOAuthRedirectUri = exports.OXY_OAUTH_RETURN_PATH_STORAGE_KEY = exports.OXY_OAUTH_REDIRECT_URI_STORAGE_KEY = exports.OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY = exports.OXY_OAUTH_STATE_STORAGE_KEY = exports.OXY_AUTHORIZE_URL = exports.DEFAULT_OAUTH_SCOPE = exports.generatePkcePair = exports.generateOAuthState = exports.computeCodeChallenge = exports.buildOAuthAuthorizeUrl = exports.CENTRAL_IDP_APEX = exports.AUTH_WEB_ORIGIN = exports.getAccountColor = exports.formatPublicKeyHandle = exports.getAccountFallbackHandle = exports.getAccountDisplayName = exports.createQuickAccount = exports.buildAccountsArray = exports.updateAvatarVisibility = exports.isDev = exports.consoleSink = exports.resetLoggerConfig = exports.getLoggerConfig = exports.configureLogger = exports.createLogger = exports.logger = exports.normalizeMultilineText = void 0;
|
|
24
|
+
exports.SUPPORTED_LANGUAGES = exports.TopicSource = exports.TopicType = exports.SECURITY_EVENT_SEVERITY_MAP = exports.sessionsArraysEqual = exports.normalizeAndSortSessions = exports.mergeSessions = exports.authenticatedApiCall = exports.withAuthErrorHandling = exports.isAuthenticationError = exports.ensureValidToken = exports.AuthenticationFailedError = exports.SessionSyncRequiredError = exports.normalizeProfileLinks = exports.getNormalizedUserHandle = exports.getCanonicalUserHandle = exports.normalizeUserIdentityOrNull = exports.normalizeUserIdentity = exports.getNormalizedUserId = exports.commonsDeliveryPlatform = exports.pushTargetsFromDelivery = exports.selectCommonsDelivery = exports.parseCommonsApprovalExpiresAt = exports.getCommonsApprovalBlockingReason = exports.OxyAppDataIdentifierError = exports.AccountNotOnDeviceError = exports.REPUTATION_CACHE_PREFIX = exports.StoreInstallerError = exports.setStoreInstaller = exports.getStoreInstaller = exports.kindAcceptsAccountCategories = exports.isSelectableAccountCategoryId = exports.SELECTABLE_ACCOUNT_CATEGORY_IDS = exports.MAX_ACCOUNT_CATEGORIES = exports.ACCOUNT_CATEGORY_IDS = exports.buildUserDid = exports.DirectUploadInterruptedError = exports.MAX_UPLOAD_BYTES = exports.assetUrlCacheTTL = exports.ASSET_MULTIPART_UPLOAD_MAX_BYTES = exports.sameHolderIdentity = exports.retryAfterSecondsOf = exports.ServiceAssetMetadataError = exports.AssetUrlResolutionError = exports.OxyAuthenticationTimeoutError = exports.OxyAuthenticationError = exports.OxyApiError = exports.OXY_CLOUD_URL = exports.OXY_API_URL = exports.OxyServices = void 0;
|
|
25
|
+
exports.translate = exports.withRetry = exports.delay = exports.shouldAllowRequest = exports.recordSuccess = exports.recordFailure = exports.calculateBackoffInterval = exports.createCircuitBreakerState = exports.DEFAULT_CIRCUIT_BREAKER_CONFIG = exports.isRetryableError = exports.isNetworkError = exports.isServerError = exports.isRateLimitError = exports.isNotFoundError = exports.isForbiddenError = exports.isUnauthorizedError = exports.isAlreadyRegisteredError = exports.getErrorMessage = exports.getErrorStatus = exports.HttpStatus = exports.getSystemColorScheme = exports.systemPrefersDarkMode = exports.getOppositeTheme = exports.normalizeColorScheme = exports.normalizeTheme = exports.getContrastTextColor = exports.isLightColor = exports.withOpacity = exports.rgbToHex = exports.hexToRgb = exports.lightenColor = exports.darkenColor = exports.isWebBrowser = exports.isAndroid = exports.isIOS = exports.isNative = exports.isWeb = exports.setPlatformOS = exports.getPlatformOS = exports.coerceToSupportedLocale = exports.getPrimaryLanguage = exports.getUserLanguages = exports.isRTLLocale = exports.getNativeLanguageName = exports.getLanguageName = exports.getLanguageMetadata = exports.isSupportedLocale = exports.normalizeLocale = exports.getBaseLanguage = exports.FALLBACK_LOCALE = void 0;
|
|
26
|
+
exports.validateAndSanitizeUserInput = exports.isValidObjectId = exports.sanitizeHTML = exports.sanitizeString = exports.isValidFileType = exports.isValidFileSize = exports.isValidDate = exports.isValidURL = exports.isValidUUID = exports.isValidObject = exports.isValidArray = exports.isRequiredBoolean = exports.isRequiredNumber = exports.isRequiredString = exports.DISPLAY_NAME_UNFLANKED_SEPARATOR_SOURCE = exports.DISPLAY_NAME_ORPHANED_MARK_SOURCE = exports.DISPLAY_NAME_DISALLOWED_SOURCE = exports.DISPLAY_NAME_ALLOWED_SCRIPTS = exports.accountNameInvalidKey = exports.accountNameInvalidMessage = exports.accountNameMaxLength = exports.isValidAccountName = exports.isValidDisplayName = exports.isValidPassword = exports.isValidEmail = exports.ACCOUNT_TITLE_INVALID_MESSAGE = exports.DISPLAY_NAME_INVALID_MESSAGE = exports.MAX_DISPLAY_NAME_LENGTH = exports.PASSWORD_REGEX = exports.EMAIL_REGEX = exports.retryAsync = exports.validateRequiredFields = exports.parseHttpErrorBody = exports.isHttpRequestError = exports.handleHttpError = exports.createApiError = exports.ErrorCodes = exports.safeJsonParse = exports.buildPaginationParams = exports.buildUrl = exports.buildSearchParams = exports.buildQueryParams = exports.trustTierLabel = exports.reputationCategoryLabel = exports.accountRoleLabel = exports.accountCategoryLabel = exports.getLocalesVersion = exports.subscribeLocales = exports.isLocaleLoaded = exports.loadLocale = void 0;
|
|
27
|
+
exports.packageInfo = exports.consumeOAuthReturnPath = exports.persistOAuthReturnPath = exports.clearOAuthHandshake = exports.readOAuthHandshake = exports.persistOAuthHandshake = exports.canonicalizeOAuthRedirectUri = exports.normalizeOAuthRedirectUri = exports.OXY_OAUTH_RETURN_PATH_STORAGE_KEY = exports.OXY_OAUTH_REDIRECT_URI_STORAGE_KEY = exports.OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY = exports.OXY_OAUTH_STATE_STORAGE_KEY = exports.OXY_AUTHORIZE_URL = exports.DEFAULT_OAUTH_SCOPE = exports.generatePkcePair = exports.generateOAuthState = exports.computeCodeChallenge = exports.buildOAuthAuthorizeUrl = exports.CENTRAL_IDP_APEX = exports.AUTH_WEB_ORIGIN = exports.getAccountColor = exports.formatPublicKeyHandle = exports.getAccountFallbackHandle = exports.getAccountDisplayName = exports.createQuickAccount = exports.buildAccountsArray = exports.updateAvatarVisibility = exports.isDev = exports.consoleSink = exports.resetLoggerConfig = exports.getLoggerConfig = exports.configureLogger = exports.createLogger = exports.logger = exports.normalizeMultilineText = exports.normalizeInlineText = void 0;
|
|
28
28
|
// The crypto shim MUST stay the first import. The root itself ships no crypto
|
|
29
29
|
// library (`__tests__/rootEntryGraph.test.ts`), but `@oxy.so/protocol`'s root
|
|
30
30
|
// entry (reached by the OAuth/PKCE and device helpers) does, and Metro does not
|
|
@@ -44,6 +44,7 @@ Object.defineProperty(exports, "OxyAuthenticationError", { enumerable: true, get
|
|
|
44
44
|
Object.defineProperty(exports, "OxyAuthenticationTimeoutError", { enumerable: true, get: function () { return OxyServices_errors_1.OxyAuthenticationTimeoutError; } });
|
|
45
45
|
Object.defineProperty(exports, "AssetUrlResolutionError", { enumerable: true, get: function () { return OxyServices_errors_1.AssetUrlResolutionError; } });
|
|
46
46
|
Object.defineProperty(exports, "ServiceAssetMetadataError", { enumerable: true, get: function () { return OxyServices_errors_1.ServiceAssetMetadataError; } });
|
|
47
|
+
Object.defineProperty(exports, "retryAfterSecondsOf", { enumerable: true, get: function () { return OxyServices_errors_1.retryAfterSecondsOf; } });
|
|
47
48
|
var holderEvidence_1 = require("./api/holderEvidence");
|
|
48
49
|
Object.defineProperty(exports, "sameHolderIdentity", { enumerable: true, get: function () { return holderEvidence_1.sameHolderIdentity; } });
|
|
49
50
|
var assets_1 = require("./api/assets");
|
|
@@ -325,59 +325,42 @@ class OxyServer extends OxyServices_1.OxyServices {
|
|
|
325
325
|
throw new Error('Requester context requires an authenticated service');
|
|
326
326
|
return new requesterClient_1.OxyRequesterClient(this.baseURL, this.cloudURL, service, proof);
|
|
327
327
|
}
|
|
328
|
+
// ── Server-side namespaces ───────────────────────────────────────────────
|
|
328
329
|
/** The account graph, plus the service-only `accessFor` membership verdict. */
|
|
329
330
|
get accounts() {
|
|
330
|
-
|
|
331
|
-
this._serverAccounts = new namespaces_1.ServerAccountsApi(this.context);
|
|
332
|
-
return this._serverAccounts;
|
|
331
|
+
return this.namespace('accounts', (context) => new namespaces_1.ServerAccountsApi(context));
|
|
333
332
|
}
|
|
334
333
|
/** Files, plus the service-only metadata and linked-URL lookups. */
|
|
335
334
|
get assets() {
|
|
336
|
-
|
|
337
|
-
this._serverAssets = new namespaces_1.ServerAssetsApi(this.context);
|
|
338
|
-
return this._serverAssets;
|
|
335
|
+
return this.namespace('assets', (context) => new namespaces_1.ServerAssetsApi(context));
|
|
339
336
|
}
|
|
340
337
|
/** The inbox and push tokens, plus `create`. */
|
|
341
338
|
get notifications() {
|
|
342
|
-
|
|
343
|
-
this._serverNotifications = new namespaces_1.ServerNotificationsApi(this.context);
|
|
344
|
-
return this._serverNotifications;
|
|
339
|
+
return this.namespace('notifications', (context) => new namespaces_1.ServerNotificationsApi(context));
|
|
345
340
|
}
|
|
346
341
|
/** Linked external accounts, plus `forUser`. */
|
|
347
342
|
get linkedAccounts() {
|
|
348
|
-
|
|
349
|
-
this._serverLinkedAccounts = new namespaces_1.ServerLinkedAccountsApi(this.context);
|
|
350
|
-
return this._serverLinkedAccounts;
|
|
343
|
+
return this.namespace('linkedAccounts', (context) => new namespaces_1.ServerLinkedAccountsApi(context));
|
|
351
344
|
}
|
|
352
345
|
/** Delegated capabilities, with the service-only authority calls. */
|
|
353
346
|
get agency() {
|
|
354
|
-
|
|
355
|
-
this._serverAgency = new namespaces_1.ServerAgencyApi(this.context);
|
|
356
|
-
return this._serverAgency;
|
|
347
|
+
return this.namespace('agency', (context) => new namespaces_1.ServerAgencyApi(context));
|
|
357
348
|
}
|
|
358
349
|
/** Application management, plus the receiver-bound Alia machine-key lookup. */
|
|
359
350
|
get apps() {
|
|
360
|
-
|
|
361
|
-
this._serverApps = new namespaces_1.ServerAppsApi(this.context);
|
|
362
|
-
return this._serverApps;
|
|
351
|
+
return this.namespace('apps', (context) => new namespaces_1.ServerAppsApi(context));
|
|
363
352
|
}
|
|
364
353
|
/** Reputation reads, plus `award`. */
|
|
365
354
|
get reputation() {
|
|
366
|
-
|
|
367
|
-
this._serverReputation = new namespaces_1.ServerReputationApi(this.context);
|
|
368
|
-
return this._serverReputation;
|
|
355
|
+
return this.namespace('reputation', (context) => new namespaces_1.ServerReputationApi(context));
|
|
369
356
|
}
|
|
370
357
|
/** Policy reads, plus the consumer-service decision lanes (ADR 0041). */
|
|
371
358
|
get safety() {
|
|
372
|
-
|
|
373
|
-
this._serverSafety = new safety_1.ServerSafetyApi(this.context);
|
|
374
|
-
return this._serverSafety;
|
|
359
|
+
return this.namespace('safety', (context) => new safety_1.ServerSafetyApi(context));
|
|
375
360
|
}
|
|
376
361
|
/** Session state, plus validation through a reviewed supervised action. */
|
|
377
362
|
get session() {
|
|
378
|
-
|
|
379
|
-
this._serverSession = new session_1.ServerSessionApi(this.context, this.sessionValidationTtlMs, () => this.revocationFeedEpoch());
|
|
380
|
-
return this._serverSession;
|
|
363
|
+
return this.namespace('session', (context) => new session_1.ServerSessionApi(context, this.sessionValidationTtlMs, () => this.revocationFeedEpoch()));
|
|
381
364
|
}
|
|
382
365
|
/** Close the revocation feed along with the HTTP client. */
|
|
383
366
|
dispose() {
|
|
@@ -9,6 +9,7 @@ const externalMediaRequester_1 = require("./externalMediaRequester");
|
|
|
9
9
|
* `oxy.assets`.
|
|
10
10
|
*/
|
|
11
11
|
const contracts_1 = require("@oxy.so/contracts");
|
|
12
|
+
const callOptions_1 = require("../client/callOptions");
|
|
12
13
|
const requesterContext_1 = require("./requesterContext");
|
|
13
14
|
const assets_1 = require("../api/assets");
|
|
14
15
|
const largeUpload_1 = require("./largeUpload");
|
|
@@ -33,6 +34,14 @@ const SERVICE_ASSET_METADATA_BY_SHA_CHUNK_SIZE = 100;
|
|
|
33
34
|
const SERVICE_LINKED_DOWNLOAD_URL_CHUNK_SIZE = 25;
|
|
34
35
|
/** Server-side cap of `POST`/`DELETE /assets/service/references`. */
|
|
35
36
|
const SERVICE_REFERENCES_CHUNK_SIZE = 100;
|
|
37
|
+
/** Server-side cap of `POST /assets/service/federation/delete`. */
|
|
38
|
+
const SERVICE_FEDERATED_DELETE_CHUNK_SIZE = 20;
|
|
39
|
+
const FEDERATED_DELETE_RESULTS = new Set([
|
|
40
|
+
'deleted',
|
|
41
|
+
'not_found',
|
|
42
|
+
'in_use',
|
|
43
|
+
'forbidden',
|
|
44
|
+
]);
|
|
36
45
|
/** Server-side cap of `POST /assets/service/app/import/batch`. */
|
|
37
46
|
const SERVICE_IMPORT_BATCH_SIZE = 50;
|
|
38
47
|
/** An import fetches a remote source (up to 60 s server-side): a longer per-attempt timeout. */
|
|
@@ -61,14 +70,6 @@ const SERVICE_RECONCILE_RATE_LIMIT_MAX_WAIT_MS = 60000;
|
|
|
61
70
|
const SHA256_HEX_PATTERN = /^[a-f0-9]{64}$/;
|
|
62
71
|
/** A header value fetch accepts on every runtime: printable ASCII only. */
|
|
63
72
|
const HEADER_SAFE_PATTERN = /^[\x20-\x7e]*$/;
|
|
64
|
-
/** The `RequestOptions` half of {@link ServiceCallOptions}, only the fields that were set. */
|
|
65
|
-
function serviceCallRequestOptions(options) {
|
|
66
|
-
return {
|
|
67
|
-
...(options.signal ? { signal: options.signal } : {}),
|
|
68
|
-
...(options.timeout !== undefined ? { timeout: options.timeout } : {}),
|
|
69
|
-
...(options.retry !== undefined ? { retry: options.retry } : {}),
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
73
|
/** The headers both streamed service uploads share. */
|
|
73
74
|
function uploadHeaders(mime, description) {
|
|
74
75
|
return {
|
|
@@ -185,6 +186,80 @@ class ServerAssetsApi extends assets_1.AssetsApi {
|
|
|
185
186
|
*/
|
|
186
187
|
reconcile: (entityType, items, options = {}) => this.reconcileReferences(entityType, items, options),
|
|
187
188
|
};
|
|
189
|
+
/**
|
|
190
|
+
* The federation media cache (`/assets/service/cache`): copies of remote
|
|
191
|
+
* media this application serves from Oxy's CDN instead of the remote host,
|
|
192
|
+
* in a reserved namespace that only holds what may be evicted. Every file
|
|
193
|
+
* is public by construction (`https://cloud.oxy.so/<id>`, `publicUrl`).
|
|
194
|
+
*
|
|
195
|
+
* - `upload` (`files:write`): the bytes as the whole body — a Blob from
|
|
196
|
+
* `fs.openAsBlob(path)` streams a file from disk; image, video or audio
|
|
197
|
+
* only (415), at most Oxy's cache size (413). Sent once.
|
|
198
|
+
* - `delete` (`federation:write`): evict one cache object. 404 when it is
|
|
199
|
+
* gone, 403 for anything outside the cache namespace — a service token can
|
|
200
|
+
* never delete media a person owns.
|
|
201
|
+
*
|
|
202
|
+
* A refusal for this application's media-write budget is a 429 whose
|
|
203
|
+
* `retryAfterSeconds` ({@link retryAfterSecondsOf}) says when to write again.
|
|
204
|
+
*/
|
|
205
|
+
this.mediaCache = {
|
|
206
|
+
upload: (bytes, mime, options = {}) => lane(this.ctx).request('POST', '/assets/service/cache', bytes, {
|
|
207
|
+
headers: uploadHeaders(mime, options),
|
|
208
|
+
cache: false,
|
|
209
|
+
retry: false,
|
|
210
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
211
|
+
...(options.timeout ? { timeout: options.timeout } : {}),
|
|
212
|
+
}),
|
|
213
|
+
delete: async (fileId) => {
|
|
214
|
+
await lane(this.ctx).request('DELETE', `/assets/service/cache/${encodeURIComponent(fileId)}`, undefined, { cache: false });
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
/**
|
|
218
|
+
* Durable federated media (`/assets/service/federation`): a remote post's
|
|
219
|
+
* media re-hosted as a normal public file owned by the federated Oxy user
|
|
220
|
+
* it came from — outside the cache namespace, so a stored post may
|
|
221
|
+
* reference it for good.
|
|
222
|
+
*
|
|
223
|
+
* - `upload` (`files:write`): `ownerUserId` must be an existing federated
|
|
224
|
+
* user (403). Idempotent per (owner, application, bytes): the existing
|
|
225
|
+
* file with `deduplicated: true`, which other posts may share — check
|
|
226
|
+
* your own references before deleting it. Bytes another owner or
|
|
227
|
+
* application holds are a 409 `FEDERATED_MEDIA_OWNED_ELSEWHERE`. Sent once.
|
|
228
|
+
* - `delete` (`files:write` + `federation:write`): one answer per distinct
|
|
229
|
+
* id, in order, sent 20 at a time — only federated media this application
|
|
230
|
+
* uploaded is ever deleted (`forbidden` otherwise), and bytes another
|
|
231
|
+
* account holds are kept (`in_use`). Every step is idempotent, so a failed
|
|
232
|
+
* call is retried whole.
|
|
233
|
+
*
|
|
234
|
+
* Budget refusals are 429s, as for {@link mediaCache}.
|
|
235
|
+
*/
|
|
236
|
+
this.federatedMedia = {
|
|
237
|
+
upload: async (ownerUserId, bytes, mime, options = {}) => {
|
|
238
|
+
const res = await lane(this.ctx).request('POST', '/assets/service/federation', bytes, {
|
|
239
|
+
headers: { ...uploadHeaders(mime, options), 'x-owner-user-id': ownerUserId },
|
|
240
|
+
cache: false,
|
|
241
|
+
retry: false,
|
|
242
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
243
|
+
...(options.timeout ? { timeout: options.timeout } : {}),
|
|
244
|
+
});
|
|
245
|
+
return { file: res.file, deduplicated: res.deduplicated === true };
|
|
246
|
+
},
|
|
247
|
+
delete: async (fileIds) => {
|
|
248
|
+
const results = [];
|
|
249
|
+
for (const part of chunk(uniqueIds(fileIds), SERVICE_FEDERATED_DELETE_CHUNK_SIZE)) {
|
|
250
|
+
const res = await lane(this.ctx).request('POST', '/assets/service/federation/delete', { ids: part }, { cache: false });
|
|
251
|
+
const asked = new Set(part);
|
|
252
|
+
for (const entry of Array.isArray(res?.results) ? res.results : []) {
|
|
253
|
+
// An id not asked, or a result this build does not know, is no
|
|
254
|
+
// answer: the caller sees the id unanswered and retries it.
|
|
255
|
+
if (asked.has(entry?.id) && FEDERATED_DELETE_RESULTS.has(entry?.result)) {
|
|
256
|
+
results.push({ id: entry.id, result: entry.result });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return results;
|
|
261
|
+
},
|
|
262
|
+
};
|
|
188
263
|
/**
|
|
189
264
|
* Per-play, revocable streaming access (ADR 0043 stage 4c). A play session
|
|
190
265
|
* is ONE file's HLS ladder (a video's, or an audio file's audio-only one)
|
|
@@ -330,7 +405,7 @@ class ServerAssetsApi extends assets_1.AssetsApi {
|
|
|
330
405
|
const chunk = references
|
|
331
406
|
.slice(start, start + SERVICE_REFERENCES_CHUNK_SIZE)
|
|
332
407
|
.map(({ fileId, entityType, entityId }) => ({ fileId, entityType, entityId }));
|
|
333
|
-
const res = await lane(this.ctx).request(method, '/assets/service/references', { references: chunk },
|
|
408
|
+
const res = await (0, callOptions_1.sendWithCallOptions)({ cache: false }, options, (requestOptions) => lane(this.ctx).request(method, '/assets/service/references', { references: chunk }, requestOptions));
|
|
334
409
|
results.push(...res.results);
|
|
335
410
|
}
|
|
336
411
|
return results;
|
|
@@ -1037,26 +1112,10 @@ async function waitingOutRateLimits(send) {
|
|
|
1037
1112
|
}
|
|
1038
1113
|
/** How long to wait after a refusal: what Oxy said, else the doubling fallback; capped. */
|
|
1039
1114
|
function rateLimitWaitMs(error, refusals) {
|
|
1040
|
-
const said =
|
|
1115
|
+
const said = (0, OxyServices_errors_1.retryAfterSecondsOf)(error);
|
|
1041
1116
|
const wait = said !== undefined ? said * 1000 : SERVICE_RECONCILE_RATE_LIMIT_FALLBACK_MS * 2 ** refusals;
|
|
1042
1117
|
return Math.min(wait, SERVICE_RECONCILE_RATE_LIMIT_MAX_WAIT_MS);
|
|
1043
1118
|
}
|
|
1044
|
-
/** `details.retryAfterSeconds` of a refusal (`RateLimitedError` on the server), if any. */
|
|
1045
|
-
function retryAfterSeconds(error) {
|
|
1046
|
-
if (!error || typeof error !== 'object')
|
|
1047
|
-
return undefined;
|
|
1048
|
-
const candidates = [
|
|
1049
|
-
error.details,
|
|
1050
|
-
error.response
|
|
1051
|
-
?.data?.details,
|
|
1052
|
-
];
|
|
1053
|
-
for (const details of candidates) {
|
|
1054
|
-
const seconds = details?.retryAfterSeconds;
|
|
1055
|
-
if (typeof seconds === 'number' && Number.isFinite(seconds) && seconds > 0)
|
|
1056
|
-
return seconds;
|
|
1057
|
-
}
|
|
1058
|
-
return undefined;
|
|
1059
|
-
}
|
|
1060
1119
|
async function waitForCommit(service, path, sessionId) {
|
|
1061
1120
|
const deadline = Date.now() + SERVICE_RECONCILE_COMMIT_WAIT_MS;
|
|
1062
1121
|
let interval = assetJobs_1.ASSET_JOB_POLL_INITIAL_MS;
|
|
@@ -15,9 +15,7 @@ class OxyRequesterClient extends OxyServices_1.OxyServices {
|
|
|
15
15
|
return this.requesterLane.request(method, url, data, options);
|
|
16
16
|
}
|
|
17
17
|
get assets() {
|
|
18
|
-
|
|
19
|
-
this.requesterAssets = new namespaces_1.ServerAssetsApi(this.context);
|
|
20
|
-
return this.requesterAssets;
|
|
18
|
+
return this.namespace('assets', (context) => new namespaces_1.ServerAssetsApi(context));
|
|
21
19
|
}
|
|
22
20
|
}
|
|
23
21
|
exports.OxyRequesterClient = OxyRequesterClient;
|