@oxy.so/contracts 1.4.0 → 1.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/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/linkedAccounts.js +93 -0
- package/dist/cjs/notifications.js +81 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/linkedAccounts.js +90 -0
- package/dist/esm/notifications.js +78 -0
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/linkedAccounts.d.ts +341 -0
- package/dist/types/notifications.d.ts +93 -0
- package/package.json +1 -1
package/dist/cjs/index.js
CHANGED
|
@@ -697,3 +697,5 @@ Object.defineProperty(exports, "inboxSmartRepliesResponseSchema", { enumerable:
|
|
|
697
697
|
Object.defineProperty(exports, "inboxThreadSummaryResponseSchema", { enumerable: true, get: function () { return inbox_1.inboxThreadSummaryResponseSchema; } });
|
|
698
698
|
Object.defineProperty(exports, "inboxInferenceStreamEventSchema", { enumerable: true, get: function () { return inbox_1.inboxInferenceStreamEventSchema; } });
|
|
699
699
|
__exportStar(require("./externalIdentity"), exports);
|
|
700
|
+
__exportStar(require("./linkedAccounts"), exports);
|
|
701
|
+
__exportStar(require("./notifications"), exports);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Wire contract for Oxy linked accounts — external accounts (any Mastodon-API
|
|
4
|
+
* server, Bluesky) a LOCAL Oxy user has proven they own by completing an OAuth
|
|
5
|
+
* authorization there.
|
|
6
|
+
*
|
|
7
|
+
* Every response schema is `.strict()` and carries no field a third-party token
|
|
8
|
+
* could occupy: Oxy uses the OAuth flow only to learn which account authorized
|
|
9
|
+
* it, then discards the token. See `docs/identity/linked-accounts.md`.
|
|
10
|
+
*
|
|
11
|
+
* Platform-agnostic — zod only, no react/react-native/expo.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.LINKED_ACCOUNT_CALLBACK_ERRORS = exports.serviceLinkedAccountListResponseSchema = exports.serviceLinkedAccountSchema = exports.linkedAccountListResponseSchema = exports.completeLinkedAccountResponseSchema = exports.linkedAccountSchema = exports.completeLinkedAccountRequestSchema = exports.startLinkedAccountResponseSchema = exports.startLinkedAccountRequestSchema = exports.linkedAccountNetworkSchema = exports.LINKED_ACCOUNT_NETWORKS = void 0;
|
|
15
|
+
const zod_1 = require("zod");
|
|
16
|
+
exports.LINKED_ACCOUNT_NETWORKS = ['activitypub', 'atproto'];
|
|
17
|
+
exports.linkedAccountNetworkSchema = zod_1.z.enum(exports.LINKED_ACCOUNT_NETWORKS);
|
|
18
|
+
/**
|
|
19
|
+
* `POST /linked-accounts/:network/start`.
|
|
20
|
+
*
|
|
21
|
+
* - `instance` (activitypub): `mastodon.social`, `https://mastodon.social` or
|
|
22
|
+
* `@user@mastodon.social`.
|
|
23
|
+
* - `handle` (atproto): a handle (`alice.bsky.social`) or a DID.
|
|
24
|
+
* - `returnTo` + `clientId`: where the browser lands afterwards, with
|
|
25
|
+
* `?link_code=` or `?link_error=`. `returnTo` must exactly match a redirect
|
|
26
|
+
* URI registered on the TRUSTED (first-party) application `clientId` names.
|
|
27
|
+
*/
|
|
28
|
+
exports.startLinkedAccountRequestSchema = zod_1.z
|
|
29
|
+
.object({
|
|
30
|
+
instance: zod_1.z.string().trim().min(1).max(320).optional(),
|
|
31
|
+
handle: zod_1.z.string().trim().min(1).max(320).optional(),
|
|
32
|
+
clientId: zod_1.z.string().trim().min(1).max(256),
|
|
33
|
+
returnTo: zod_1.z.string().trim().min(1).max(2048),
|
|
34
|
+
})
|
|
35
|
+
.strict();
|
|
36
|
+
exports.startLinkedAccountResponseSchema = zod_1.z
|
|
37
|
+
.object({
|
|
38
|
+
authorizeUrl: zod_1.z.string().url(),
|
|
39
|
+
expiresAt: zod_1.z.string().datetime(),
|
|
40
|
+
})
|
|
41
|
+
.strict();
|
|
42
|
+
/**
|
|
43
|
+
* `POST /linked-accounts/complete`, with the session of the user who STARTED
|
|
44
|
+
* the flow: `code` is the one-time `link_code` the callback appended to
|
|
45
|
+
* `returnTo`. It expires five minutes after the callback. Presented by any
|
|
46
|
+
* other user it is refused (403) and burned.
|
|
47
|
+
*/
|
|
48
|
+
exports.completeLinkedAccountRequestSchema = zod_1.z
|
|
49
|
+
.object({ code: zod_1.z.string().trim().min(1).max(256) })
|
|
50
|
+
.strict();
|
|
51
|
+
/** One live linked account, as its owner sees it. */
|
|
52
|
+
exports.linkedAccountSchema = zod_1.z
|
|
53
|
+
.object({
|
|
54
|
+
id: zod_1.z.string().min(1),
|
|
55
|
+
network: exports.linkedAccountNetworkSchema,
|
|
56
|
+
/** `username@domain` for ActivityPub; the DID for atproto. */
|
|
57
|
+
accountKey: zod_1.z.string().min(1),
|
|
58
|
+
/** ActivityPub actor id, or the DID for atproto. */
|
|
59
|
+
actorUri: zod_1.z.string().min(1),
|
|
60
|
+
handle: zod_1.z.string().min(1),
|
|
61
|
+
host: zod_1.z.string().min(1),
|
|
62
|
+
proofMethod: zod_1.z.literal('oauth'),
|
|
63
|
+
verifiedAt: zod_1.z.string().datetime(),
|
|
64
|
+
createdAt: zod_1.z.string().datetime(),
|
|
65
|
+
})
|
|
66
|
+
.strict();
|
|
67
|
+
exports.completeLinkedAccountResponseSchema = zod_1.z
|
|
68
|
+
.object({ linkedAccount: exports.linkedAccountSchema })
|
|
69
|
+
.strict();
|
|
70
|
+
exports.linkedAccountListResponseSchema = zod_1.z
|
|
71
|
+
.object({ linkedAccounts: zod_1.z.array(exports.linkedAccountSchema) })
|
|
72
|
+
.strict();
|
|
73
|
+
/**
|
|
74
|
+
* `GET /linked-accounts/by-user/:userId` (service token with the privileged
|
|
75
|
+
* `linked-accounts:read`). Adds `federatedUserId`: the FEDERATED shadow user Oxy
|
|
76
|
+
* already holds for that external account, if any — the anchor for adopting
|
|
77
|
+
* content Oxy federated in before the account was linked.
|
|
78
|
+
*/
|
|
79
|
+
exports.serviceLinkedAccountSchema = exports.linkedAccountSchema
|
|
80
|
+
.extend({ federatedUserId: zod_1.z.string().min(1).nullable() })
|
|
81
|
+
.strict();
|
|
82
|
+
exports.serviceLinkedAccountListResponseSchema = zod_1.z
|
|
83
|
+
.object({ userId: zod_1.z.string().min(1), linkedAccounts: zod_1.z.array(exports.serviceLinkedAccountSchema) })
|
|
84
|
+
.strict();
|
|
85
|
+
/**
|
|
86
|
+
* Error codes the callback appends as `?link_error=<code>` to `returnTo`. An
|
|
87
|
+
* account already linked to someone else is reported by `/complete` (409).
|
|
88
|
+
*/
|
|
89
|
+
exports.LINKED_ACCOUNT_CALLBACK_ERRORS = [
|
|
90
|
+
'access_denied',
|
|
91
|
+
'verification_failed',
|
|
92
|
+
'provider_unavailable',
|
|
93
|
+
];
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The closed set of Oxy in-app notification types — owned here so the API's
|
|
4
|
+
* CHECK constraint (`notifications.type`), its request validation and the SDK
|
|
5
|
+
* all read one tuple.
|
|
6
|
+
*
|
|
7
|
+
* `system` is a message from an Oxy service about the recipient's OWN account
|
|
8
|
+
* (Oxy Move's "your migration finished"): the actor is the recipient, and the
|
|
9
|
+
* entity is their profile or an `app` id. It is the only type an actor does not
|
|
10
|
+
* cause.
|
|
11
|
+
*
|
|
12
|
+
* Platform-agnostic — zod only, no react/react-native/expo.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.createOxyNotificationRequestSchema = exports.OXY_NOTIFICATION_URL_MAX = exports.OXY_SYSTEM_NOTIFICATION_MESSAGE_MAX = exports.OXY_SYSTEM_NOTIFICATION_TITLE_MAX = exports.OXY_NOTIFICATION_ENTITY_TYPES = exports.OXY_NOTIFICATION_TYPES = void 0;
|
|
16
|
+
const zod_1 = require("zod");
|
|
17
|
+
exports.OXY_NOTIFICATION_TYPES = [
|
|
18
|
+
'like',
|
|
19
|
+
'reply',
|
|
20
|
+
'mention',
|
|
21
|
+
'follow',
|
|
22
|
+
'repost',
|
|
23
|
+
'quote',
|
|
24
|
+
'welcome',
|
|
25
|
+
'system',
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* What a notification's `entityId` names.
|
|
29
|
+
*
|
|
30
|
+
* `app` means `entityId` is an OPAQUE id in the notifying application's own
|
|
31
|
+
* namespace (Oxy Move's migration job id). Oxy never resolves it; it exists so
|
|
32
|
+
* a `system` notification about something that is not a post or a profile does
|
|
33
|
+
* not have to pretend to be one. Valid only with `type: 'system'`.
|
|
34
|
+
*/
|
|
35
|
+
exports.OXY_NOTIFICATION_ENTITY_TYPES = ['post', 'reply', 'profile', 'app'];
|
|
36
|
+
/** Length caps on the text a `system` notification carries (enforced by CHECKs too). */
|
|
37
|
+
exports.OXY_SYSTEM_NOTIFICATION_TITLE_MAX = 120;
|
|
38
|
+
exports.OXY_SYSTEM_NOTIFICATION_MESSAGE_MAX = 500;
|
|
39
|
+
exports.OXY_NOTIFICATION_URL_MAX = 2048;
|
|
40
|
+
/**
|
|
41
|
+
* `POST /notifications` (service token with `notifications:write`).
|
|
42
|
+
*
|
|
43
|
+
* Only a `system` notification carries text: `title` and `message` are REQUIRED
|
|
44
|
+
* for it and stored, and `url` is an optional deep link (https, or a custom
|
|
45
|
+
* scheme registered as a redirect URI on the calling application). For every
|
|
46
|
+
* other type the client renders from `type` + entity, and `title` / `message` /
|
|
47
|
+
* `data` are accepted but discarded, as they always were.
|
|
48
|
+
*
|
|
49
|
+
* `system` notifications share the duplicate guard (recipient, actor, type,
|
|
50
|
+
* entityId): use a caller-unique `entityId` (a job id) for each distinct event.
|
|
51
|
+
*/
|
|
52
|
+
exports.createOxyNotificationRequestSchema = zod_1.z
|
|
53
|
+
.object({
|
|
54
|
+
recipientId: zod_1.z.string().min(1),
|
|
55
|
+
actorId: zod_1.z.string().min(1),
|
|
56
|
+
type: zod_1.z.enum(exports.OXY_NOTIFICATION_TYPES),
|
|
57
|
+
entityId: zod_1.z.string().min(1),
|
|
58
|
+
entityType: zod_1.z.enum(exports.OXY_NOTIFICATION_ENTITY_TYPES),
|
|
59
|
+
title: zod_1.z.string().trim().min(1).max(exports.OXY_SYSTEM_NOTIFICATION_TITLE_MAX).optional(),
|
|
60
|
+
message: zod_1.z.string().trim().min(1).max(exports.OXY_SYSTEM_NOTIFICATION_MESSAGE_MAX).optional(),
|
|
61
|
+
url: zod_1.z.string().trim().min(1).max(exports.OXY_NOTIFICATION_URL_MAX).optional(),
|
|
62
|
+
data: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
|
|
63
|
+
})
|
|
64
|
+
.superRefine((value, context) => {
|
|
65
|
+
if (value.type === 'system') {
|
|
66
|
+
if (value.actorId !== value.recipientId)
|
|
67
|
+
context.addIssue({ code: zod_1.z.ZodIssueCode.custom, path: ['actorId'], message: 'a system notification is from the recipient\'s own account' });
|
|
68
|
+
if (!value.title)
|
|
69
|
+
context.addIssue({ code: zod_1.z.ZodIssueCode.custom, path: ['title'], message: 'title is required for a system notification' });
|
|
70
|
+
if (!value.message)
|
|
71
|
+
context.addIssue({ code: zod_1.z.ZodIssueCode.custom, path: ['message'], message: 'message is required for a system notification' });
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
if (value.url !== undefined) {
|
|
75
|
+
context.addIssue({ code: zod_1.z.ZodIssueCode.custom, path: ['url'], message: 'url is only stored for a system notification' });
|
|
76
|
+
}
|
|
77
|
+
if (value.entityType === 'app') {
|
|
78
|
+
context.addIssue({ code: zod_1.z.ZodIssueCode.custom, path: ['entityType'], message: "entityType 'app' is only valid for a system notification" });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|