@atproto/oauth-scopes 0.0.2 → 0.2.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.
Files changed (169) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/atproto-oauth-scope.d.ts +17 -0
  3. package/dist/atproto-oauth-scope.d.ts.map +1 -0
  4. package/dist/atproto-oauth-scope.js +67 -0
  5. package/dist/atproto-oauth-scope.js.map +1 -0
  6. package/dist/index.d.ts +9 -13
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +9 -13
  9. package/dist/index.js.map +1 -1
  10. package/dist/lib/lexicon.d.ts +2 -0
  11. package/dist/lib/lexicon.d.ts.map +1 -0
  12. package/dist/lib/lexicon.js +3 -0
  13. package/dist/lib/lexicon.js.map +1 -0
  14. package/dist/lib/mime.d.ts +1 -1
  15. package/dist/lib/mime.d.ts.map +1 -1
  16. package/dist/lib/mime.js +2 -0
  17. package/dist/lib/mime.js.map +1 -1
  18. package/dist/lib/nsid.d.ts +2 -2
  19. package/dist/lib/nsid.d.ts.map +1 -1
  20. package/dist/lib/nsid.js +4 -6
  21. package/dist/lib/nsid.js.map +1 -1
  22. package/dist/lib/parser.d.ts +29 -0
  23. package/dist/lib/parser.d.ts.map +1 -0
  24. package/dist/lib/parser.js +152 -0
  25. package/dist/lib/parser.js.map +1 -0
  26. package/dist/lib/resource-permission.d.ts +10 -0
  27. package/dist/lib/resource-permission.d.ts.map +1 -0
  28. package/dist/lib/resource-permission.js +3 -0
  29. package/dist/lib/resource-permission.js.map +1 -0
  30. package/dist/lib/syntax-lexicon.d.ts +26 -0
  31. package/dist/lib/syntax-lexicon.d.ts.map +1 -0
  32. package/dist/lib/syntax-lexicon.js +58 -0
  33. package/dist/lib/syntax-lexicon.js.map +1 -0
  34. package/dist/lib/syntax-string.d.ts +16 -0
  35. package/dist/lib/syntax-string.d.ts.map +1 -0
  36. package/dist/lib/syntax-string.js +121 -0
  37. package/dist/lib/syntax-string.js.map +1 -0
  38. package/dist/lib/syntax.d.ts +23 -0
  39. package/dist/lib/syntax.d.ts.map +1 -0
  40. package/dist/lib/syntax.js +22 -0
  41. package/dist/lib/syntax.js.map +1 -0
  42. package/dist/lib/util.d.ts +5 -1
  43. package/dist/lib/util.d.ts.map +1 -1
  44. package/dist/lib/util.js +8 -12
  45. package/dist/lib/util.js.map +1 -1
  46. package/dist/scope-permissions-transition.d.ts +15 -0
  47. package/dist/scope-permissions-transition.d.ts.map +1 -0
  48. package/dist/{permission-set-transition.js → scope-permissions-transition.js} +5 -5
  49. package/dist/scope-permissions-transition.js.map +1 -0
  50. package/dist/scope-permissions.d.ts +22 -0
  51. package/dist/scope-permissions.d.ts.map +1 -0
  52. package/dist/{permission-set.js → scope-permissions.js} +20 -16
  53. package/dist/scope-permissions.js.map +1 -0
  54. package/dist/scopes/account-permission.d.ts +35 -0
  55. package/dist/scopes/account-permission.d.ts.map +1 -0
  56. package/dist/scopes/account-permission.js +71 -0
  57. package/dist/scopes/account-permission.js.map +1 -0
  58. package/dist/scopes/blob-permission.d.ts +27 -0
  59. package/dist/scopes/blob-permission.d.ts.map +1 -0
  60. package/dist/scopes/blob-permission.js +86 -0
  61. package/dist/scopes/blob-permission.js.map +1 -0
  62. package/dist/scopes/identity-permission.d.ts +25 -0
  63. package/dist/scopes/identity-permission.d.ts.map +1 -0
  64. package/dist/scopes/identity-permission.js +53 -0
  65. package/dist/scopes/identity-permission.js.map +1 -0
  66. package/dist/scopes/include-scope.d.ts +54 -0
  67. package/dist/scopes/include-scope.d.ts.map +1 -0
  68. package/dist/scopes/include-scope.js +156 -0
  69. package/dist/scopes/include-scope.js.map +1 -0
  70. package/dist/scopes/repo-permission.d.ts +40 -0
  71. package/dist/scopes/repo-permission.d.ts.map +1 -0
  72. package/dist/scopes/repo-permission.js +101 -0
  73. package/dist/scopes/repo-permission.js.map +1 -0
  74. package/dist/scopes/rpc-permission.d.ts +38 -0
  75. package/dist/scopes/rpc-permission.d.ts.map +1 -0
  76. package/dist/scopes/rpc-permission.js +81 -0
  77. package/dist/scopes/rpc-permission.js.map +1 -0
  78. package/dist/scopes-set.d.ts +12 -1
  79. package/dist/scopes-set.d.ts.map +1 -1
  80. package/dist/scopes-set.js +49 -3
  81. package/dist/scopes-set.js.map +1 -1
  82. package/package.json +7 -3
  83. package/src/atproto-oauth-scope.ts +79 -0
  84. package/src/index.ts +10 -14
  85. package/src/lib/lexicon.ts +1 -0
  86. package/src/lib/mime.ts +2 -1
  87. package/src/lib/nsid.ts +5 -6
  88. package/src/lib/parser.ts +176 -0
  89. package/src/lib/resource-permission.ts +10 -0
  90. package/src/lib/syntax-lexicon.ts +55 -0
  91. package/src/lib/syntax-string.test.ts +130 -0
  92. package/src/lib/syntax-string.ts +132 -0
  93. package/src/lib/syntax.test.ts +43 -0
  94. package/src/lib/syntax.ts +47 -0
  95. package/src/lib/util.ts +11 -12
  96. package/src/{permission-set-transition.test.ts → scope-permissions-transition.test.ts} +33 -20
  97. package/src/{permission-set-transition.ts → scope-permissions-transition.ts} +11 -11
  98. package/src/{permission-set.test.ts → scope-permissions.test.ts} +77 -35
  99. package/src/scope-permissions.ts +91 -0
  100. package/src/{resources/account-scope.test.ts → scopes/account-permission.test.ts} +45 -33
  101. package/src/scopes/account-permission.ts +75 -0
  102. package/src/{resources/blob-scope.test.ts → scopes/blob-permission.test.ts} +31 -23
  103. package/src/scopes/blob-permission.ts +105 -0
  104. package/src/{resources/identity-scope.test.ts → scopes/identity-permission.test.ts} +13 -13
  105. package/src/scopes/identity-permission.ts +54 -0
  106. package/src/scopes/include-scope.test.ts +626 -0
  107. package/src/scopes/include-scope.ts +168 -0
  108. package/src/{resources/repo-scope.test.ts → scopes/repo-permission.test.ts} +77 -65
  109. package/src/scopes/repo-permission.ts +111 -0
  110. package/src/scopes/rpc-permission.test.ts +323 -0
  111. package/src/scopes/rpc-permission.ts +85 -0
  112. package/src/scopes-set.test.ts +5 -5
  113. package/src/scopes-set.ts +79 -5
  114. package/tsconfig.build.tsbuildinfo +1 -1
  115. package/tsconfig.tests.tsbuildinfo +1 -1
  116. package/dist/lib/did.d.ts +0 -3
  117. package/dist/lib/did.d.ts.map +0 -1
  118. package/dist/lib/did.js +0 -6
  119. package/dist/lib/did.js.map +0 -1
  120. package/dist/parser.d.ts +0 -31
  121. package/dist/parser.d.ts.map +0 -1
  122. package/dist/parser.js +0 -118
  123. package/dist/parser.js.map +0 -1
  124. package/dist/permission-set-transition.d.ts +0 -15
  125. package/dist/permission-set-transition.d.ts.map +0 -1
  126. package/dist/permission-set-transition.js.map +0 -1
  127. package/dist/permission-set.d.ts +0 -22
  128. package/dist/permission-set.d.ts.map +0 -1
  129. package/dist/permission-set.js.map +0 -1
  130. package/dist/resources/account-scope.d.ts +0 -35
  131. package/dist/resources/account-scope.d.ts.map +0 -1
  132. package/dist/resources/account-scope.js +0 -60
  133. package/dist/resources/account-scope.js.map +0 -1
  134. package/dist/resources/blob-scope.d.ts +0 -25
  135. package/dist/resources/blob-scope.d.ts.map +0 -1
  136. package/dist/resources/blob-scope.js +0 -74
  137. package/dist/resources/blob-scope.js.map +0 -1
  138. package/dist/resources/identity-scope.d.ts +0 -25
  139. package/dist/resources/identity-scope.d.ts.map +0 -1
  140. package/dist/resources/identity-scope.js +0 -46
  141. package/dist/resources/identity-scope.js.map +0 -1
  142. package/dist/resources/repo-scope.d.ts +0 -37
  143. package/dist/resources/repo-scope.d.ts.map +0 -1
  144. package/dist/resources/repo-scope.js +0 -92
  145. package/dist/resources/repo-scope.js.map +0 -1
  146. package/dist/resources/rpc-scope.d.ts +0 -31
  147. package/dist/resources/rpc-scope.d.ts.map +0 -1
  148. package/dist/resources/rpc-scope.js +0 -74
  149. package/dist/resources/rpc-scope.js.map +0 -1
  150. package/dist/syntax.d.ts +0 -76
  151. package/dist/syntax.d.ts.map +0 -1
  152. package/dist/syntax.js +0 -249
  153. package/dist/syntax.js.map +0 -1
  154. package/dist/utilities.d.ts +0 -17
  155. package/dist/utilities.d.ts.map +0 -1
  156. package/dist/utilities.js +0 -108
  157. package/dist/utilities.js.map +0 -1
  158. package/src/lib/did.ts +0 -3
  159. package/src/parser.ts +0 -150
  160. package/src/permission-set.ts +0 -78
  161. package/src/resources/account-scope.ts +0 -66
  162. package/src/resources/blob-scope.ts +0 -86
  163. package/src/resources/identity-scope.ts +0 -49
  164. package/src/resources/repo-scope.ts +0 -101
  165. package/src/resources/rpc-scope.test.ts +0 -280
  166. package/src/resources/rpc-scope.ts +0 -77
  167. package/src/syntax.test.ts +0 -203
  168. package/src/syntax.ts +0 -325
  169. package/src/utilities.ts +0 -109
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RpcPermission = exports.isAudParam = exports.isLxmParam = exports.isNsid = exports.isAtprotoAudience = void 0;
4
+ const did_1 = require("@atproto/did");
5
+ Object.defineProperty(exports, "isAtprotoAudience", { enumerable: true, get: function () { return did_1.isAtprotoAudience; } });
6
+ const nsid_js_1 = require("../lib/nsid.js");
7
+ Object.defineProperty(exports, "isNsid", { enumerable: true, get: function () { return nsid_js_1.isNsid; } });
8
+ const parser_js_1 = require("../lib/parser.js");
9
+ const syntax_string_js_1 = require("../lib/syntax-string.js");
10
+ const syntax_js_1 = require("../lib/syntax.js");
11
+ const isLxmParam = (value) => value === '*' || (0, nsid_js_1.isNsid)(value);
12
+ exports.isLxmParam = isLxmParam;
13
+ const isAudParam = (value) => value === '*' || (0, did_1.isAtprotoAudience)(value);
14
+ exports.isAudParam = isAudParam;
15
+ class RpcPermission {
16
+ constructor(aud, lxm) {
17
+ Object.defineProperty(this, "aud", {
18
+ enumerable: true,
19
+ configurable: true,
20
+ writable: true,
21
+ value: aud
22
+ });
23
+ Object.defineProperty(this, "lxm", {
24
+ enumerable: true,
25
+ configurable: true,
26
+ writable: true,
27
+ value: lxm
28
+ });
29
+ }
30
+ matches(options) {
31
+ const { aud, lxm } = this;
32
+ return ((aud === '*' || aud === options.aud) &&
33
+ (lxm.includes('*') || lxm.includes(options.lxm)));
34
+ }
35
+ toString() {
36
+ return RpcPermission.parser.format(this);
37
+ }
38
+ static fromString(scope) {
39
+ if (!(0, syntax_js_1.isScopeStringFor)(scope, 'rpc'))
40
+ return null;
41
+ const syntax = syntax_string_js_1.ScopeStringSyntax.fromString(scope);
42
+ return RpcPermission.fromSyntax(syntax);
43
+ }
44
+ static fromSyntax(syntax) {
45
+ const result = RpcPermission.parser.parse(syntax);
46
+ if (!result)
47
+ return null;
48
+ // rpc:*?aud=* is forbidden
49
+ if (result.aud === '*' && result.lxm.includes('*'))
50
+ return null;
51
+ return new RpcPermission(result.aud, result.lxm);
52
+ }
53
+ static scopeNeededFor(options) {
54
+ return RpcPermission.parser.format({
55
+ aud: options.aud,
56
+ lxm: [options.lxm],
57
+ });
58
+ }
59
+ }
60
+ exports.RpcPermission = RpcPermission;
61
+ Object.defineProperty(RpcPermission, "parser", {
62
+ enumerable: true,
63
+ configurable: true,
64
+ writable: true,
65
+ value: new parser_js_1.Parser('rpc', {
66
+ lxm: {
67
+ multiple: true,
68
+ required: true,
69
+ validate: exports.isLxmParam,
70
+ normalize: (value) => value.length > 1 && value.includes('*')
71
+ ? ['*']
72
+ : [...new Set(value)].sort(),
73
+ },
74
+ aud: {
75
+ multiple: false,
76
+ required: true,
77
+ validate: exports.isAudParam,
78
+ },
79
+ }, 'lxm')
80
+ });
81
+ //# sourceMappingURL=rpc-permission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc-permission.js","sourceRoot":"","sources":["../../src/scopes/rpc-permission.ts"],"names":[],"mappings":";;;AAAA,sCAAiE;AAOvB,kGAPhB,uBAAiB,OAOgB;AAN3D,4CAA6C;AAMgB,uFAN9C,gBAAM,OAM8C;AALnE,gDAAyC;AAEzC,8DAA2D;AAC3D,gDAA2E;AAKpE,MAAM,UAAU,GAAG,CAAC,KAAc,EAAqB,EAAE,CAC9D,KAAK,KAAK,GAAG,IAAI,IAAA,gBAAM,EAAC,KAAK,CAAC,CAAA;AADnB,QAAA,UAAU,cACS;AAEzB,MAAM,UAAU,GAAG,CAAC,KAAc,EAAqB,EAAE,CAC9D,KAAK,KAAK,GAAG,IAAI,IAAA,uBAAiB,EAAC,KAAK,CAAC,CAAA;AAD9B,QAAA,UAAU,cACoB;AAO3C,MAAa,aAAa;IAGxB,YACkB,GAA0B,EAC1B,GAA0B;QAD1C;;;;mBAAgB,GAAG;WAAuB;QAC1C;;;;mBAAgB,GAAG;WAAuB;IACzC,CAAC;IAEJ,OAAO,CAAC,OAA2B;QACjC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;QACzB,OAAO,CACL,CAAC,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC;YACpC,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAK,GAAyB,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CACxE,CAAA;IACH,CAAC;IAED,QAAQ;QACN,OAAO,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC1C,CAAC;IAuBD,MAAM,CAAC,UAAU,CAAC,KAAa;QAC7B,IAAI,CAAC,IAAA,4BAAgB,EAAC,KAAK,EAAE,KAAK,CAAC;YAAE,OAAO,IAAI,CAAA;QAChD,MAAM,MAAM,GAAG,oCAAiB,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;QAClD,OAAO,aAAa,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;IACzC,CAAC;IAED,MAAM,CAAC,UAAU,CAAC,MAA0B;QAC1C,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;QACjD,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAA;QAExB,2BAA2B;QAC3B,IAAI,MAAM,CAAC,GAAG,KAAK,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAA;QAE/D,OAAO,IAAI,aAAa,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAAA;IAClD,CAAC;IAED,MAAM,CAAC,cAAc,CAAC,OAA2B;QAC/C,OAAO,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC;YACjC,GAAG,EAAE,OAAO,CAAC,GAAsB;YACnC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAW,CAAC;SAC3B,CAAC,CAAA;IACJ,CAAC;;AA9DH,sCA+DC;AA3C2B;;;;WAAS,IAAI,kBAAM,CAC3C,KAAK,EACL;QACE,GAAG,EAAE;YACH,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,kBAAU;YACpB,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CACnB,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;gBACrC,CAAC,CAAE,CAAC,GAAG,CAAW;gBAClB,CAAC,CAAE,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAwB;SACxD;QACD,GAAG,EAAE;YACH,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,kBAAU;SACrB;KACF,EACD,KAAK,CACN;EAnB+B,CAmB/B"}
@@ -1,6 +1,17 @@
1
1
  import { ScopeMissingError } from './scope-missing-error.js';
2
- import { ScopeMatchingOptionsByResource } from './utilities.js';
2
+ import { AccountPermissionMatch } from './scopes/account-permission.js';
3
+ import { BlobPermissionMatch } from './scopes/blob-permission.js';
4
+ import { IdentityPermissionMatch } from './scopes/identity-permission.js';
5
+ import { RepoPermissionMatch } from './scopes/repo-permission.js';
6
+ import { RpcPermissionMatch } from './scopes/rpc-permission.js';
3
7
  export { ScopeMissingError };
8
+ export type ScopeMatchingOptionsByResource = {
9
+ account: AccountPermissionMatch;
10
+ identity: IdentityPermissionMatch;
11
+ repo: RepoPermissionMatch;
12
+ rpc: RpcPermissionMatch;
13
+ blob: BlobPermissionMatch;
14
+ };
4
15
  /**
5
16
  * Utility class to manage a set of scopes and check if they match specific
6
17
  * options for a given resource.
@@ -1 +1 @@
1
- {"version":3,"file":"scopes-set.d.ts","sourceRoot":"","sources":["../src/scopes-set.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EACL,8BAA8B,EAG/B,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EAAE,iBAAiB,EAAE,CAAA;AAE5B;;;GAGG;AACH,qBAAa,SAAU,SAAQ,GAAG,CAAC,MAAM,CAAC;IACxC;;;OAGG;IACI,OAAO,CAAC,CAAC,SAAS,MAAM,8BAA8B,EAC3D,QAAQ,EAAE,CAAC,EACX,OAAO,EAAE,8BAA8B,CAAC,CAAC,CAAC,GACzC,OAAO;IAOH,MAAM,CAAC,CAAC,SAAS,MAAM,8BAA8B,EAC1D,QAAQ,EAAE,CAAC,EACX,OAAO,EAAE,8BAA8B,CAAC,CAAC,CAAC;IAQrC,IAAI,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO;IAK7C,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO;IAK7C,MAAM,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO;IAIrC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,CAAC;IAIvC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS;CAG9C"}
1
+ {"version":3,"file":"scopes-set.d.ts","sourceRoot":"","sources":["../src/scopes-set.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAEL,sBAAsB,EACvB,MAAM,gCAAgC,CAAA;AACvC,OAAO,EAEL,mBAAmB,EACpB,MAAM,6BAA6B,CAAA;AACpC,OAAO,EAEL,uBAAuB,EACxB,MAAM,iCAAiC,CAAA;AACxC,OAAO,EAEL,mBAAmB,EACpB,MAAM,6BAA6B,CAAA;AACpC,OAAO,EAAiB,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAE9E,OAAO,EAAE,iBAAiB,EAAE,CAAA;AAE5B,MAAM,MAAM,8BAA8B,GAAG;IAC3C,OAAO,EAAE,sBAAsB,CAAA;IAC/B,QAAQ,EAAE,uBAAuB,CAAA;IACjC,IAAI,EAAE,mBAAmB,CAAA;IACzB,GAAG,EAAE,kBAAkB,CAAA;IACvB,IAAI,EAAE,mBAAmB,CAAA;CAC1B,CAAA;AAED;;;GAGG;AACH,qBAAa,SAAU,SAAQ,GAAG,CAAC,MAAM,CAAC;IACxC;;;OAGG;IACI,OAAO,CAAC,CAAC,SAAS,MAAM,8BAA8B,EAC3D,QAAQ,EAAE,CAAC,EACX,OAAO,EAAE,8BAA8B,CAAC,CAAC,CAAC,GACzC,OAAO;IAOH,MAAM,CAAC,CAAC,SAAS,MAAM,8BAA8B,EAC1D,QAAQ,EAAE,CAAC,EACX,OAAO,EAAE,8BAA8B,CAAC,CAAC,CAAC;IAQrC,IAAI,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO;IAK7C,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO;IAK7C,MAAM,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO;IAIrC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,CAAC;IAIvC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS;CAG9C"}
@@ -3,7 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ScopesSet = exports.ScopeMissingError = void 0;
4
4
  const scope_missing_error_js_1 = require("./scope-missing-error.js");
5
5
  Object.defineProperty(exports, "ScopeMissingError", { enumerable: true, get: function () { return scope_missing_error_js_1.ScopeMissingError; } });
6
- const utilities_js_1 = require("./utilities.js");
6
+ const account_permission_js_1 = require("./scopes/account-permission.js");
7
+ const blob_permission_js_1 = require("./scopes/blob-permission.js");
8
+ const identity_permission_js_1 = require("./scopes/identity-permission.js");
9
+ const repo_permission_js_1 = require("./scopes/repo-permission.js");
10
+ const rpc_permission_js_1 = require("./scopes/rpc-permission.js");
7
11
  /**
8
12
  * Utility class to manage a set of scopes and check if they match specific
9
13
  * options for a given resource.
@@ -15,14 +19,14 @@ class ScopesSet extends Set {
15
19
  */
16
20
  matches(resource, options) {
17
21
  for (const scope of this) {
18
- if ((0, utilities_js_1.scopeMatches)(scope, resource, options))
22
+ if (permissionScopeMatches(scope, resource, options))
19
23
  return true;
20
24
  }
21
25
  return false;
22
26
  }
23
27
  assert(resource, options) {
24
28
  if (!this.matches(resource, options)) {
25
- const scope = (0, utilities_js_1.scopeNeededFor)(resource, options);
29
+ const scope = scopeNeededFor(resource, options);
26
30
  throw new scope_missing_error_js_1.ScopeMissingError(scope);
27
31
  }
28
32
  }
@@ -52,4 +56,46 @@ class ScopesSet extends Set {
52
56
  }
53
57
  }
54
58
  exports.ScopesSet = ScopesSet;
59
+ function scopeNeededFor(resource, options) {
60
+ switch (resource) {
61
+ case 'account':
62
+ return account_permission_js_1.AccountPermission.scopeNeededFor(options);
63
+ case 'identity':
64
+ return identity_permission_js_1.IdentityPermission.scopeNeededFor(options);
65
+ case 'repo':
66
+ return repo_permission_js_1.RepoPermission.scopeNeededFor(options);
67
+ case 'rpc':
68
+ return rpc_permission_js_1.RpcPermission.scopeNeededFor(options);
69
+ case 'blob':
70
+ return blob_permission_js_1.BlobPermission.scopeNeededFor(options);
71
+ }
72
+ // @ts-expect-error
73
+ throw new TypeError(`Unknown resource: ${resource}`);
74
+ }
75
+ function permissionScopeMatches(scope, resource, options) {
76
+ // @NOTE we might want to cache the parsed scopes though, in practice, a
77
+ // single scope is unlikely to be parsed multiple times during a single
78
+ // request.
79
+ const permission = parsePermissionScope(resource, scope);
80
+ if (!permission)
81
+ return false;
82
+ // @ts-expect-error
83
+ return permission.matches(options);
84
+ }
85
+ function parsePermissionScope(resource, scope) {
86
+ switch (resource) {
87
+ case 'account':
88
+ return account_permission_js_1.AccountPermission.fromString(scope);
89
+ case 'identity':
90
+ return identity_permission_js_1.IdentityPermission.fromString(scope);
91
+ case 'repo':
92
+ return repo_permission_js_1.RepoPermission.fromString(scope);
93
+ case 'rpc':
94
+ return rpc_permission_js_1.RpcPermission.fromString(scope);
95
+ case 'blob':
96
+ return blob_permission_js_1.BlobPermission.fromString(scope);
97
+ default:
98
+ return null;
99
+ }
100
+ }
55
101
  //# sourceMappingURL=scopes-set.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"scopes-set.js","sourceRoot":"","sources":["../src/scopes-set.ts"],"names":[],"mappings":";;;AAAA,qEAA4D;AAOnD,kGAPA,0CAAiB,OAOA;AAN1B,iDAIuB;AAIvB;;;GAGG;AACH,MAAa,SAAU,SAAQ,GAAW;IACxC;;;OAGG;IACI,OAAO,CACZ,QAAW,EACX,OAA0C;QAE1C,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;YACzB,IAAI,IAAA,2BAAY,EAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC;gBAAE,OAAO,IAAI,CAAA;QACzD,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;IAEM,MAAM,CACX,QAAW,EACX,OAA0C;QAE1C,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC;YACrC,MAAM,KAAK,GAAG,IAAA,6BAAc,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;YAC/C,MAAM,IAAI,0CAAiB,CAAC,KAAK,CAAC,CAAA;QACpC,CAAC;IACH,CAAC;IAEM,IAAI,CAAC,EAA8B;QACxC,KAAK,MAAM,KAAK,IAAI,IAAI;YAAE,IAAI,EAAE,CAAC,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAA;QACpD,OAAO,KAAK,CAAA;IACd,CAAC;IAEM,KAAK,CAAC,EAA8B;QACzC,KAAK,MAAM,KAAK,IAAI,IAAI;YAAE,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAA;QACtD,OAAO,IAAI,CAAA;IACb,CAAC;IAEM,CAAC,MAAM,CAAC,EAA8B;QAC3C,KAAK,MAAM,KAAK,IAAI,IAAI;YAAE,IAAI,EAAE,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAA;IACtD,CAAC;IAEM,CAAC,GAAG,CAAI,EAAwB;QACrC,KAAK,MAAM,KAAK,IAAI,IAAI;YAAE,MAAM,EAAE,CAAC,KAAK,CAAC,CAAA;IAC3C,CAAC;IAED,MAAM,CAAC,UAAU,CAAC,MAAe;QAC/B,OAAO,IAAI,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;IAC1C,CAAC;CACF;AA9CD,8BA8CC"}
1
+ {"version":3,"file":"scopes-set.js","sourceRoot":"","sources":["../src/scopes-set.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/oauth-scopes",
3
- "version": "0.0.2",
3
+ "version": "0.2.0",
4
4
  "license": "MIT",
5
5
  "description": "A library for manipulating and validating ATproto OAuth scopes in TypeScript.",
6
6
  "keywords": [
@@ -24,13 +24,17 @@
24
24
  "default": "./dist/index.js"
25
25
  }
26
26
  },
27
- "dependencies": {},
27
+ "dependencies": {
28
+ "@atproto/did": "^0.2.0",
29
+ "@atproto/lexicon": "^0.5.1",
30
+ "@atproto/syntax": "^0.4.1"
31
+ },
28
32
  "devDependencies": {
29
33
  "jest": "^28.1.2",
30
34
  "typescript": "^5.6.3"
31
35
  },
32
36
  "scripts": {
33
- "build": "tsc --build tsconfig.json",
37
+ "build": "tsc --build tsconfig.build.json",
34
38
  "test": "jest"
35
39
  }
36
40
  }
@@ -0,0 +1,79 @@
1
+ import { ScopeStringFor, isScopeStringFor } from './lib/syntax.js'
2
+ import { isNonNullable } from './lib/util.js'
3
+ import { AccountPermission } from './scopes/account-permission.js'
4
+ import { BlobPermission } from './scopes/blob-permission.js'
5
+ import { IdentityPermission } from './scopes/identity-permission.js'
6
+ import { IncludeScope } from './scopes/include-scope.js'
7
+ import { RepoPermission } from './scopes/repo-permission.js'
8
+ import { RpcPermission } from './scopes/rpc-permission.js'
9
+
10
+ export { type ScopeStringFor, isScopeStringFor }
11
+
12
+ export const STATIC_SCOPE_VALUES = Object.freeze([
13
+ 'atproto',
14
+ 'transition:email',
15
+ 'transition:generic',
16
+ 'transition:chat.bsky',
17
+ ] as const)
18
+
19
+ export type StaticScopeValue = (typeof STATIC_SCOPE_VALUES)[number]
20
+ export function isStaticScopeValue(value: string): value is StaticScopeValue {
21
+ return (STATIC_SCOPE_VALUES as readonly string[]).includes(value)
22
+ }
23
+
24
+ export type AtprotoOauthScope =
25
+ | StaticScopeValue
26
+ | ScopeStringFor<'account'>
27
+ | ScopeStringFor<'blob'>
28
+ | ScopeStringFor<'identity'>
29
+ | ScopeStringFor<'include'>
30
+ | ScopeStringFor<'repo'>
31
+ | ScopeStringFor<'rpc'>
32
+
33
+ /**
34
+ * @note This function does not only verify the scope string format (with
35
+ * {@link isScopeStringFor}), but also checks if the provided parameters are
36
+ * valid according to the respective scope syntax definition. This allows
37
+ * excluding scopes that cannot be fully interpreted by the current version of
38
+ * the code.
39
+ */
40
+ export function isAtprotoOauthScope(value: string): value is AtprotoOauthScope {
41
+ return (
42
+ isStaticScopeValue(value) ||
43
+ AccountPermission.fromString(value) != null ||
44
+ BlobPermission.fromString(value) != null ||
45
+ IdentityPermission.fromString(value) != null ||
46
+ IncludeScope.fromString(value) != null ||
47
+ RepoPermission.fromString(value) != null ||
48
+ RpcPermission.fromString(value) != null
49
+ )
50
+ }
51
+
52
+ export function normalizeAtprotoOauthScope(scope: string) {
53
+ return scope
54
+ .split(' ')
55
+ .map(normalizeAtprotoOauthScopeValue)
56
+ .filter(isNonNullable)
57
+ .sort()
58
+ .join(' ')
59
+ }
60
+
61
+ export function normalizeAtprotoOauthScopeValue(
62
+ value: string,
63
+ ): AtprotoOauthScope | null {
64
+ if (isStaticScopeValue(value)) return value
65
+
66
+ for (const Scope of [
67
+ AccountPermission,
68
+ BlobPermission,
69
+ IdentityPermission,
70
+ IncludeScope,
71
+ RepoPermission,
72
+ RpcPermission,
73
+ ]) {
74
+ const parsed = Scope.fromString(value)
75
+ if (parsed) return parsed.toString()
76
+ }
77
+
78
+ return null
79
+ }
package/src/index.ts CHANGED
@@ -1,17 +1,13 @@
1
+ export * from './atproto-oauth-scope.js'
2
+
1
3
  export * from './scope-missing-error.js'
2
- export * from './permission-set.js'
3
- export * from './permission-set-transition.js'
4
+ export * from './scope-permissions-transition.js'
5
+ export * from './scope-permissions.js'
4
6
  export * from './scopes-set.js'
5
- export * from './parser.js'
6
- export * from './syntax.js'
7
- export * from './utilities.js'
8
-
9
- export * from './resources/account-scope.js'
10
- export * from './resources/blob-scope.js'
11
- export * from './resources/identity-scope.js'
12
- export * from './resources/repo-scope.js'
13
- export * from './resources/rpc-scope.js'
14
7
 
15
- export * from './lib/mime.js'
16
- export * from './lib/did.js'
17
- export * from './lib/nsid.js'
8
+ export * from './scopes/account-permission.js'
9
+ export * from './scopes/blob-permission.js'
10
+ export * from './scopes/identity-permission.js'
11
+ export * from './scopes/include-scope.js'
12
+ export * from './scopes/repo-permission.js'
13
+ export * from './scopes/rpc-permission.js'
@@ -0,0 +1 @@
1
+ export type { LexPermission, LexPermissionSet } from '@atproto/lexicon'
package/src/lib/mime.ts CHANGED
@@ -20,7 +20,8 @@ export function isMime(value: string): value is Mime {
20
20
 
21
21
  export type Accept = '*/*' | `${string}/*` | Mime
22
22
 
23
- export function isAccept(value: string): value is Accept {
23
+ export function isAccept(value: unknown): value is Accept {
24
+ if (typeof value !== 'string') return false
24
25
  if (value === '*/*') return true // Fast path for the most common case
25
26
  if (!isStringSlashString(value)) return false
26
27
  return !value.includes('*') || value.endsWith('/*')
package/src/lib/nsid.ts CHANGED
@@ -1,6 +1,5 @@
1
- export type NSID = `${string}.${string}`
2
- export const isNSID = (value: string): value is NSID =>
3
- value.includes('.') &&
4
- !value.includes(' ') &&
5
- !value.startsWith('.') &&
6
- !value.endsWith('.')
1
+ import { isValidNsid } from '@atproto/syntax'
2
+
3
+ export type Nsid = `${string}.${string}.${string}`
4
+ export const isNsid = (v: unknown): v is Nsid =>
5
+ typeof v === 'string' && isValidNsid(v)
@@ -0,0 +1,176 @@
1
+ import { ScopeStringSyntax } from './syntax-string.js'
2
+ import { NeRoArray, ParamValue, ScopeSyntax } from './syntax.js'
3
+
4
+ type InferParamPredicate<T extends (value: ParamValue) => boolean> =
5
+ T extends ((value: ParamValue) => value is infer U extends ParamValue)
6
+ ? U
7
+ : ParamValue
8
+
9
+ type ParamsSchema = Record<
10
+ string,
11
+ | {
12
+ multiple: false
13
+ required: boolean
14
+ default?: ParamValue
15
+ normalize?: (value: ParamValue) => ParamValue
16
+ validate: (value: ParamValue) => boolean
17
+ }
18
+ | {
19
+ multiple: true
20
+ required: boolean
21
+ default?: NeRoArray<ParamValue>
22
+ normalize?: (value: NeRoArray<ParamValue>) => NeRoArray<ParamValue>
23
+ validate: (value: ParamValue) => boolean
24
+ }
25
+ >
26
+
27
+ type InferParams<S extends ParamsSchema> = {
28
+ [K in keyof S]:
29
+ | (S[K]['required'] extends true
30
+ ? never
31
+ : 'default' extends keyof S[K]
32
+ ? S[K]['default']
33
+ : undefined)
34
+ | (S[K]['multiple'] extends true
35
+ ? NeRoArray<InferParamPredicate<S[K]['validate']>>
36
+ : InferParamPredicate<S[K]['validate']>)
37
+ } & NonNullable<unknown>
38
+
39
+ export class Parser<P extends string, S extends ParamsSchema> {
40
+ public readonly schemaKeys: ReadonlySet<keyof S & string>
41
+
42
+ constructor(
43
+ public readonly prefix: P,
44
+ public readonly schema: S,
45
+ public readonly positionalName?: keyof S & string,
46
+ ) {
47
+ this.schemaKeys = new Set(Object.keys(schema))
48
+ }
49
+
50
+ format(values: InferParams<S>) {
51
+ const params = new URLSearchParams()
52
+ let positional: string | undefined = undefined
53
+
54
+ for (const key of this.schemaKeys) {
55
+ const value = values[key]
56
+ // Ignore undefined values
57
+ if (value === undefined) continue
58
+
59
+ const schema = this.schema[key]
60
+
61
+ // Normalize the value if a normalization function is provided
62
+ const normalized = schema.normalize
63
+ ? schema.normalize(value as any)
64
+ : value
65
+
66
+ // Ignore values that are equal to the default value
67
+ if (!schema.required) {
68
+ if (schema.default === normalized) continue
69
+ if (
70
+ schema.multiple &&
71
+ schema.default &&
72
+ arrayParamEquals(schema.default, normalized as NeRoArray<string>)
73
+ ) {
74
+ continue
75
+ }
76
+ }
77
+
78
+ if (Array.isArray(normalized)) {
79
+ if (key === this.positionalName && normalized.length === 1) {
80
+ positional = String(normalized[0]!)
81
+ } else {
82
+ // remove duplicates
83
+ const unique = new Set(normalized.map(String))
84
+ for (const v of unique) params.append(key, v)
85
+ }
86
+ } else {
87
+ if (key === this.positionalName) {
88
+ positional = String(normalized)
89
+ } else {
90
+ params.set(key, String(normalized))
91
+ }
92
+ }
93
+ }
94
+
95
+ return new ScopeStringSyntax(this.prefix, positional, params).toString()
96
+ }
97
+
98
+ // @NOTE If we needed to ever have more detailed reason as to why parsing
99
+ // fails, this function could easily be updated to return a
100
+ // ValidationResult<T> type that explains the reason for failure.
101
+ parse(syntax: ScopeSyntax<P>) {
102
+ // @NOTE no need to check prefix, since the typing (P generic) already
103
+ // ensures it matches
104
+
105
+ for (const key of syntax.keys()) {
106
+ if (!this.schemaKeys.has(key)) return null
107
+ }
108
+
109
+ const result: Record<
110
+ string,
111
+ undefined | ParamValue | NeRoArray<ParamValue>
112
+ > = Object.create(null)
113
+
114
+ for (const key of this.schemaKeys) {
115
+ const definition = this.schema[key]
116
+
117
+ const param = definition.multiple
118
+ ? syntax.getMulti(key)
119
+ : syntax.getSingle(key)
120
+
121
+ if (param === null) {
122
+ return null // Value is not valid
123
+ } else if (param !== undefined) {
124
+ if (key === this.positionalName && syntax.positional !== undefined) {
125
+ // Positional parameter cannot be used with named parameters
126
+ return null
127
+ }
128
+
129
+ if (definition.multiple) {
130
+ // Empty array is not valid
131
+ if (!(param as ParamValue[]).length) return null
132
+ if (!(param as ParamValue[]).every(definition.validate)) {
133
+ return null
134
+ }
135
+ } else {
136
+ if (!definition.validate(param as ParamValue)) {
137
+ return null
138
+ }
139
+ }
140
+
141
+ result[key] = param as ParamValue | NeRoArray<ParamValue>
142
+ } else if (
143
+ key === this.positionalName &&
144
+ syntax.positional !== undefined
145
+ ) {
146
+ // No named parameters found, but there is a positional parameter
147
+ const { positional } = syntax
148
+ if (!definition.validate(positional)) {
149
+ return null
150
+ }
151
+ result[key] = definition.multiple ? [positional] : positional
152
+ } else if (definition.required) {
153
+ return null
154
+ } else {
155
+ result[key] = definition.default
156
+ }
157
+ }
158
+
159
+ return result as InferParams<S>
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Two param arrays are considered equal if they contain the same values,
165
+ * regardless of the order and duplicates.
166
+ * @param a - The first array to compare.
167
+ * @param b - The second array to compare.
168
+ */
169
+ function arrayParamEquals(
170
+ a: readonly unknown[],
171
+ b: readonly unknown[],
172
+ ): boolean {
173
+ for (const item of a) if (!b.includes(item)) return false
174
+ for (const item of b) if (!a.includes(item)) return false
175
+ return true
176
+ }
@@ -0,0 +1,10 @@
1
+ import { ScopeStringFor } from './syntax.js'
2
+ import { Matchable } from './util.js'
3
+
4
+ /**
5
+ * Interface destined to provide consistency across parsed permission scopes for
6
+ * resources (blob, repo, etc.).
7
+ */
8
+ export interface ResourcePermission<R extends string, T> extends Matchable<T> {
9
+ toString(): ScopeStringFor<R>
10
+ }
@@ -0,0 +1,55 @@
1
+ import { LexPermission } from './lexicon.js'
2
+ import { ScopeSyntax } from './syntax.js'
3
+
4
+ /**
5
+ * Translates a {@link LexPermission} into a {@link ScopeSyntax}.
6
+ */
7
+ export class LexPermissionSyntax<P extends string = string>
8
+ implements ScopeSyntax<P>
9
+ {
10
+ constructor(
11
+ readonly lexPermission: Readonly<LexPermission & { resource: P }>,
12
+ ) {}
13
+
14
+ get prefix() {
15
+ return this.lexPermission.resource
16
+ }
17
+
18
+ get positional() {
19
+ return undefined
20
+ }
21
+
22
+ get(key: string) {
23
+ // Ignore reserved keywords
24
+ if (key === 'type') return undefined
25
+ if (key === 'resource') return undefined
26
+
27
+ // Ignore inherited properties (toString(), etc.)
28
+ if (!Object.hasOwn(this.lexPermission, key)) return undefined
29
+
30
+ return this.lexPermission[key]
31
+ }
32
+
33
+ *keys() {
34
+ for (const key of Object.keys(this.lexPermission)) {
35
+ if (this.get(key) !== undefined) yield key
36
+ }
37
+ }
38
+
39
+ getSingle(key: string) {
40
+ const value = this.get(key)
41
+ if (Array.isArray(value)) return null
42
+ return value
43
+ }
44
+
45
+ getMulti(key: string) {
46
+ const value = this.get(key)
47
+ if (value === undefined) return undefined
48
+ if (!Array.isArray(value)) return null
49
+ return value
50
+ }
51
+
52
+ toJSON() {
53
+ return this.lexPermission
54
+ }
55
+ }