@oxyhq/contracts 0.33.0 → 0.34.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.
@@ -36,9 +36,13 @@ export const CHILD_ACCOUNT_KINDS = [
36
36
  ];
37
37
  export const childAccountKindSchema = z.enum(CHILD_ACCOUNT_KINDS);
38
38
  /**
39
- * Whether an operator may ACT AS an account of this kind switch the whole app
40
- * into it (`POST /accounts/:id/switch`) or authorise an app to act as it
41
- * (an OAuth delegated subject).
39
+ * Whether an account of this kind may be the SUBJECT OF A DELEGATION — an
40
+ * application acting as it on some person's authority. `POST /internal/accounts/
41
+ * :id/service-switch` and the OAuth delegated subject both gate on this.
42
+ *
43
+ * It is NOT the question an account switcher asks. See
44
+ * {@link isOperatorSwitchTargetKind}, and the note below on why the difference
45
+ * is `bot`.
42
46
  *
43
47
  * Two kinds are refused, for opposite reasons:
44
48
  *
@@ -55,9 +59,43 @@ export const childAccountKindSchema = z.enum(CHILD_ACCOUNT_KINDS);
55
59
  * Consumers must gate on this predicate rather than testing `kind === 'personal'`,
56
60
  * which silently admits every kind added after it was written.
57
61
  */
58
- export function isActAsEligibleKind(kind) {
62
+ export function isDelegatedActAsEligibleKind(kind) {
59
63
  return kind === 'organization' || kind === 'project' || kind === 'bot';
60
64
  }
65
+ /**
66
+ * Whether a PERSON may switch into an account of this kind — become it, in an
67
+ * account switcher, for the rest of their session.
68
+ *
69
+ * ## Why this is not the same question as {@link isDelegatedActAsEligibleKind}
70
+ *
71
+ * The two differ on exactly one kind, `bot`, and that difference is the whole
72
+ * reason both exist.
73
+ *
74
+ * **A bot is not something you become. It is something that operates on your
75
+ * behalf.** Its whole purpose is to act while nobody is present: an application
76
+ * holds a credential, names the human whose authority it borrows, and speaks as
77
+ * the bot. That is delegation, and it is what
78
+ * {@link isDelegatedActAsEligibleKind} admits it for.
79
+ *
80
+ * Handing a person the bot's seat instead inverts that. It puts a human inside
81
+ * the identity that exists to act without one, and it does so on the human's own
82
+ * device, next to their personal login — which is precisely what happened: a
83
+ * `bot` account held a live session on a person's device, offered to them by a
84
+ * switcher that had asked the delegation question by mistake.
85
+ *
86
+ * `channel` is refused here as well, for the reason set out above, and
87
+ * `personal` because assuming somebody else's login is impersonation.
88
+ *
89
+ * ## This is the narrower predicate, deliberately
90
+ *
91
+ * Everything a person may become, a service may also act as; the reverse does
92
+ * not hold. A caller that is unsure which question it is asking wants THIS one:
93
+ * being wrong here withholds an affordance, while being wrong the other way
94
+ * hands out a seat.
95
+ */
96
+ export function isOperatorSwitchTargetKind(kind) {
97
+ return kind === 'organization' || kind === 'project';
98
+ }
61
99
  /**
62
100
  * Narrow an unknown value to an {@link AccountKind}.
63
101
  *
@@ -288,7 +326,7 @@ export const accountCategoriesSchema = z
288
326
  *
289
327
  * A person has interests, not a sector — and their interests are not a
290
328
  * classification anybody else gets to read off their profile. Spelled out
291
- * positively, like {@link isActAsEligibleKind} and for the same reason: a `kind
329
+ * positively, like {@link isDelegatedActAsEligibleKind} and for the same reason: a `kind
292
330
  * !== 'personal'` test silently admits every kind invented after it was
293
331
  * written, whereas this list forces whoever adds one to decide.
294
332
  */
package/dist/esm/index.js CHANGED
@@ -9,7 +9,7 @@
9
9
  * Platform-agnostic — zod is the only runtime dependency. No react/react-native/
10
10
  * expo, no `require()` in the ESM build.
11
11
  */
12
- export { ACCOUNT_KINDS, accountKindSchema, CHILD_ACCOUNT_KINDS, childAccountKindSchema, isAccountKind, isActAsEligibleKind, ACCOUNT_CATEGORY_IDS, ACCOUNT_CATEGORY_KINDS, accountCategoriesSchema, accountCategoryIdSchema, isSelectableAccountCategoryId, kindAcceptsAccountCategories, MAX_ACCOUNT_CATEGORIES, newlyAddedRetiredCategories, RETIRED_ACCOUNT_CATEGORY_IDS, SELECTABLE_ACCOUNT_CATEGORY_IDS, createAccountRequestSchema, } from './accountGraph.js';
12
+ export { ACCOUNT_KINDS, accountKindSchema, CHILD_ACCOUNT_KINDS, childAccountKindSchema, isAccountKind, isDelegatedActAsEligibleKind, isOperatorSwitchTargetKind, ACCOUNT_CATEGORY_IDS, ACCOUNT_CATEGORY_KINDS, accountCategoriesSchema, accountCategoryIdSchema, isSelectableAccountCategoryId, kindAcceptsAccountCategories, MAX_ACCOUNT_CATEGORIES, newlyAddedRetiredCategories, RETIRED_ACCOUNT_CATEGORY_IDS, SELECTABLE_ACCOUNT_CATEGORY_IDS, createAccountRequestSchema, } from './accountGraph.js';
13
13
  export { usernameSchema, isValidUsername, stripDisallowedUsernameCharacters, USERNAME_MIN_LENGTH, USERNAME_MAX_LENGTH, USERNAME_INVALID_MESSAGE, } from './username.js';
14
14
  export {
15
15
  // Schemas