@oxy.so/core 7.17.0 → 7.18.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.
@@ -58,6 +58,19 @@ function stringArray(value) {
58
58
  : null;
59
59
  }
60
60
  const RELATIONSHIPS = ['self', 'owner', 'member'];
61
+ const APPLICATION_TYPES = [
62
+ 'first_party',
63
+ 'third_party',
64
+ 'internal',
65
+ 'system',
66
+ ];
67
+ const OFFICIAL_TYPES = ['first_party', 'internal', 'system'];
68
+ /** `deleted` is absent on purpose: Oxy answers a deleted application 404. */
69
+ const APPLICATION_STATUSES = [
70
+ 'active',
71
+ 'suspended',
72
+ 'pending_review',
73
+ ];
61
74
  /** `GET /accounts/:id` → the caller's effective access, or `null` for a malformed answer. */
62
75
  function parseAccountAccess(accountId, answer) {
63
76
  const node = record(answer);
@@ -84,11 +97,16 @@ function parseApplicationAccess(applicationId, answer) {
84
97
  if (!application || application._id !== applicationId)
85
98
  return null;
86
99
  const ownerAccountId = application.ownerAccountId;
100
+ const type = application.type;
101
+ const status = application.status;
87
102
  const membership = record(application.callerMembership);
88
103
  const permissions = stringArray(membership?.permissions);
89
104
  if (typeof ownerAccountId !== 'string' ||
90
105
  ownerAccountId === '' ||
91
106
  typeof application.isOfficial !== 'boolean' ||
107
+ typeof application.isInternal !== 'boolean' ||
108
+ !APPLICATION_TYPES.includes(type) ||
109
+ !APPLICATION_STATUSES.includes(status) ||
92
110
  !membership ||
93
111
  typeof membership.role !== 'string' ||
94
112
  membership.role === '' ||
@@ -99,7 +117,8 @@ function parseApplicationAccess(applicationId, answer) {
99
117
  return Object.freeze({
100
118
  applicationId,
101
119
  ownerAccountId,
102
- official: application.isOfficial,
120
+ official: application.isOfficial || application.isInternal || OFFICIAL_TYPES.includes(type),
121
+ status,
103
122
  role: membership.role,
104
123
  ...(0, callerAuthority_1.permissionSet)(permissions),
105
124
  });
@@ -53,6 +53,19 @@ function stringArray(value) {
53
53
  : null;
54
54
  }
55
55
  const RELATIONSHIPS = ['self', 'owner', 'member'];
56
+ const APPLICATION_TYPES = [
57
+ 'first_party',
58
+ 'third_party',
59
+ 'internal',
60
+ 'system',
61
+ ];
62
+ const OFFICIAL_TYPES = ['first_party', 'internal', 'system'];
63
+ /** `deleted` is absent on purpose: Oxy answers a deleted application 404. */
64
+ const APPLICATION_STATUSES = [
65
+ 'active',
66
+ 'suspended',
67
+ 'pending_review',
68
+ ];
56
69
  /** `GET /accounts/:id` → the caller's effective access, or `null` for a malformed answer. */
57
70
  export function parseAccountAccess(accountId, answer) {
58
71
  const node = record(answer);
@@ -79,11 +92,16 @@ export function parseApplicationAccess(applicationId, answer) {
79
92
  if (!application || application._id !== applicationId)
80
93
  return null;
81
94
  const ownerAccountId = application.ownerAccountId;
95
+ const type = application.type;
96
+ const status = application.status;
82
97
  const membership = record(application.callerMembership);
83
98
  const permissions = stringArray(membership?.permissions);
84
99
  if (typeof ownerAccountId !== 'string' ||
85
100
  ownerAccountId === '' ||
86
101
  typeof application.isOfficial !== 'boolean' ||
102
+ typeof application.isInternal !== 'boolean' ||
103
+ !APPLICATION_TYPES.includes(type) ||
104
+ !APPLICATION_STATUSES.includes(status) ||
87
105
  !membership ||
88
106
  typeof membership.role !== 'string' ||
89
107
  membership.role === '' ||
@@ -94,7 +112,8 @@ export function parseApplicationAccess(applicationId, answer) {
94
112
  return Object.freeze({
95
113
  applicationId,
96
114
  ownerAccountId,
97
- official: application.isOfficial,
115
+ official: application.isOfficial || application.isInternal || OFFICIAL_TYPES.includes(type),
116
+ status,
98
117
  role: membership.role,
99
118
  ...permissionSet(permissions),
100
119
  });
@@ -28,6 +28,7 @@
28
28
  */
29
29
  import { type AccountAccessSource, type AccountKind } from '@oxy.so/contracts';
30
30
  import type { AccountRelationship } from '../api/accounts';
31
+ import type { ApplicationStatus } from '../api/apps';
31
32
  import { type OxyCallerRequest } from './callerAuthority';
32
33
  /** What the person behind a request may do in one Oxy account. */
33
34
  export interface OxyAccountAccess {
@@ -48,8 +49,20 @@ export interface OxyApplicationAccess {
48
49
  readonly applicationId: string;
49
50
  /** The account that owns the application; the access derives from the caller's membership there. */
50
51
  readonly ownerAccountId: string;
51
- /** Whether the application is an official Oxy application. */
52
+ /**
53
+ * Whether the application is one of Oxy's own: `isOfficial`, `isInternal`,
54
+ * or typed `first_party` / `internal` / `system` — the rule oxy-api applies
55
+ * to every trust decision (`isTrustedApplication`). All of those fields are
56
+ * staff-controlled; a self-service `third_party` application is never
57
+ * official, whatever its owner sets.
58
+ */
52
59
  readonly official: boolean;
60
+ /**
61
+ * The application's lifecycle status. A `suspended` or `pending_review`
62
+ * application is still answered (its members may read it); whether it may
63
+ * act is the caller's decision.
64
+ */
65
+ readonly status: ApplicationStatus;
53
66
  /** Informational; gate on `permissions` / `has`, never on the role name. */
54
67
  readonly role: string;
55
68
  /** The application permissions (`app:read`, `app:update`, `webhooks:update`, …), sorted. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxy.so/core",
3
- "version": "7.17.0",
3
+ "version": "7.18.0",
4
4
  "description": "OxyHQ SDK Foundation — API client, authentication, cryptographic identity, and shared utilities",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -33,6 +33,7 @@ import {
33
33
  type AccountKind,
34
34
  } from '@oxy.so/contracts';
35
35
  import type { AccountRelationship } from '../api/accounts';
36
+ import type { ApplicationStatus, ApplicationType } from '../api/apps';
36
37
  import {
37
38
  accessFailure,
38
39
  CallerAuthorityCache,
@@ -63,8 +64,20 @@ export interface OxyApplicationAccess {
63
64
  readonly applicationId: string;
64
65
  /** The account that owns the application; the access derives from the caller's membership there. */
65
66
  readonly ownerAccountId: string;
66
- /** Whether the application is an official Oxy application. */
67
+ /**
68
+ * Whether the application is one of Oxy's own: `isOfficial`, `isInternal`,
69
+ * or typed `first_party` / `internal` / `system` — the rule oxy-api applies
70
+ * to every trust decision (`isTrustedApplication`). All of those fields are
71
+ * staff-controlled; a self-service `third_party` application is never
72
+ * official, whatever its owner sets.
73
+ */
67
74
  readonly official: boolean;
75
+ /**
76
+ * The application's lifecycle status. A `suspended` or `pending_review`
77
+ * application is still answered (its members may read it); whether it may
78
+ * act is the caller's decision.
79
+ */
80
+ readonly status: ApplicationStatus;
68
81
  /** Informational; gate on `permissions` / `has`, never on the role name. */
69
82
  readonly role: string;
70
83
  /** The application permissions (`app:read`, `app:update`, `webhooks:update`, …), sorted. */
@@ -117,6 +130,19 @@ function stringArray(value: unknown): string[] | null {
117
130
  }
118
131
 
119
132
  const RELATIONSHIPS: readonly AccountRelationship[] = ['self', 'owner', 'member'];
133
+ const APPLICATION_TYPES: readonly ApplicationType[] = [
134
+ 'first_party',
135
+ 'third_party',
136
+ 'internal',
137
+ 'system',
138
+ ];
139
+ const OFFICIAL_TYPES: readonly ApplicationType[] = ['first_party', 'internal', 'system'];
140
+ /** `deleted` is absent on purpose: Oxy answers a deleted application 404. */
141
+ const APPLICATION_STATUSES: readonly ApplicationStatus[] = [
142
+ 'active',
143
+ 'suspended',
144
+ 'pending_review',
145
+ ];
120
146
 
121
147
  /** `GET /accounts/:id` → the caller's effective access, or `null` for a malformed answer. */
122
148
  export function parseAccountAccess(accountId: string, answer: unknown): OxyAccountAccess | null {
@@ -144,12 +170,17 @@ export function parseApplicationAccess(
144
170
  const application = record(answer);
145
171
  if (!application || application._id !== applicationId) return null;
146
172
  const ownerAccountId = application.ownerAccountId;
173
+ const type = application.type as ApplicationType;
174
+ const status = application.status as ApplicationStatus;
147
175
  const membership = record(application.callerMembership);
148
176
  const permissions = stringArray(membership?.permissions);
149
177
  if (
150
178
  typeof ownerAccountId !== 'string' ||
151
179
  ownerAccountId === '' ||
152
180
  typeof application.isOfficial !== 'boolean' ||
181
+ typeof application.isInternal !== 'boolean' ||
182
+ !APPLICATION_TYPES.includes(type) ||
183
+ !APPLICATION_STATUSES.includes(status) ||
153
184
  !membership ||
154
185
  typeof membership.role !== 'string' ||
155
186
  membership.role === '' ||
@@ -161,7 +192,8 @@ export function parseApplicationAccess(
161
192
  return Object.freeze({
162
193
  applicationId,
163
194
  ownerAccountId,
164
- official: application.isOfficial,
195
+ official: application.isOfficial || application.isInternal || OFFICIAL_TYPES.includes(type),
196
+ status,
165
197
  role: membership.role,
166
198
  ...permissionSet(permissions),
167
199
  });