@selvajs/server 1.5.0-beta.9 → 2.0.0-beta.16

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 (143) hide show
  1. package/dist/access/guards.d.ts +0 -10
  2. package/dist/access/guards.d.ts.map +1 -1
  3. package/dist/access/guards.js +23 -26
  4. package/dist/access/guards.js.map +1 -1
  5. package/dist/api/bodies.d.ts +1 -1
  6. package/dist/api/bodies.d.ts.map +1 -1
  7. package/dist/api/bodies.js +2 -1
  8. package/dist/api/bodies.js.map +1 -1
  9. package/dist/api/idempotency.d.ts +10 -3
  10. package/dist/api/idempotency.d.ts.map +1 -1
  11. package/dist/api/idempotency.js +32 -9
  12. package/dist/api/idempotency.js.map +1 -1
  13. package/dist/api/index.d.ts +2 -2
  14. package/dist/api/index.d.ts.map +1 -1
  15. package/dist/api/index.js +2 -2
  16. package/dist/api/index.js.map +1 -1
  17. package/dist/api/request.d.ts +12 -4
  18. package/dist/api/request.d.ts.map +1 -1
  19. package/dist/api/request.js +32 -5
  20. package/dist/api/request.js.map +1 -1
  21. package/dist/api/respond.d.ts.map +1 -1
  22. package/dist/api/respond.js +11 -7
  23. package/dist/api/respond.js.map +1 -1
  24. package/dist/api/scope.d.ts +7 -0
  25. package/dist/api/scope.d.ts.map +1 -1
  26. package/dist/api/scope.js +15 -1
  27. package/dist/api/scope.js.map +1 -1
  28. package/dist/compute/idempotency.d.ts +8 -1
  29. package/dist/compute/idempotency.d.ts.map +1 -1
  30. package/dist/compute/idempotency.js +35 -5
  31. package/dist/compute/idempotency.js.map +1 -1
  32. package/dist/compute/index.d.ts +1 -1
  33. package/dist/compute/index.d.ts.map +1 -1
  34. package/dist/compute/index.js +1 -1
  35. package/dist/compute/index.js.map +1 -1
  36. package/dist/compute/limits.d.ts +0 -7
  37. package/dist/compute/limits.d.ts.map +1 -1
  38. package/dist/compute/limits.js +0 -1
  39. package/dist/compute/limits.js.map +1 -1
  40. package/dist/compute/server-config-write.d.ts +11 -0
  41. package/dist/compute/server-config-write.d.ts.map +1 -1
  42. package/dist/compute/server-config-write.js +34 -0
  43. package/dist/compute/server-config-write.js.map +1 -1
  44. package/dist/definitions/definition-service.d.ts.map +1 -1
  45. package/dist/definitions/definition-service.js +3 -2
  46. package/dist/definitions/definition-service.js.map +1 -1
  47. package/dist/definitions/index.d.ts +1 -1
  48. package/dist/definitions/index.d.ts.map +1 -1
  49. package/dist/definitions/index.js +1 -1
  50. package/dist/definitions/index.js.map +1 -1
  51. package/dist/definitions/load-for-render.d.ts +9 -0
  52. package/dist/definitions/load-for-render.d.ts.map +1 -1
  53. package/dist/definitions/load-for-render.js +1 -1
  54. package/dist/definitions/load-for-render.js.map +1 -1
  55. package/dist/definitions/visibility.d.ts.map +1 -1
  56. package/dist/definitions/visibility.js +7 -12
  57. package/dist/definitions/visibility.js.map +1 -1
  58. package/dist/handlers/apiTokens.d.ts.map +1 -1
  59. package/dist/handlers/apiTokens.js +3 -1
  60. package/dist/handlers/apiTokens.js.map +1 -1
  61. package/dist/handlers/definitionVersions.d.ts.map +1 -1
  62. package/dist/handlers/definitionVersions.js +2 -2
  63. package/dist/handlers/definitionVersions.js.map +1 -1
  64. package/dist/handlers/definitions.d.ts.map +1 -1
  65. package/dist/handlers/definitions.js +27 -15
  66. package/dist/handlers/definitions.js.map +1 -1
  67. package/dist/handlers/index.d.ts +1 -1
  68. package/dist/handlers/index.d.ts.map +1 -1
  69. package/dist/handlers/index.js +1 -1
  70. package/dist/handlers/index.js.map +1 -1
  71. package/dist/handlers/invite-lookup.d.ts.map +1 -1
  72. package/dist/handlers/invite-lookup.js +3 -5
  73. package/dist/handlers/invite-lookup.js.map +1 -1
  74. package/dist/handlers/invites.d.ts +4 -3
  75. package/dist/handlers/invites.d.ts.map +1 -1
  76. package/dist/handlers/invites.js +17 -3
  77. package/dist/handlers/invites.js.map +1 -1
  78. package/dist/handlers/oauthConsent.d.ts +25 -13
  79. package/dist/handlers/oauthConsent.d.ts.map +1 -1
  80. package/dist/handlers/oauthConsent.js +29 -13
  81. package/dist/handlers/oauthConsent.js.map +1 -1
  82. package/dist/handlers/orgAssets.d.ts.map +1 -1
  83. package/dist/handlers/orgAssets.js +2 -2
  84. package/dist/handlers/orgAssets.js.map +1 -1
  85. package/dist/handlers/orgCompute.d.ts.map +1 -1
  86. package/dist/handlers/orgCompute.js +4 -2
  87. package/dist/handlers/orgCompute.js.map +1 -1
  88. package/dist/handlers/orgMembers.d.ts.map +1 -1
  89. package/dist/handlers/orgMembers.js +76 -56
  90. package/dist/handlers/orgMembers.js.map +1 -1
  91. package/dist/handlers/ownerChanges.d.ts +8 -0
  92. package/dist/handlers/ownerChanges.d.ts.map +1 -0
  93. package/dist/handlers/ownerChanges.js +19 -0
  94. package/dist/handlers/ownerChanges.js.map +1 -0
  95. package/dist/handlers/projectMembers.d.ts.map +1 -1
  96. package/dist/handlers/projectMembers.js +20 -15
  97. package/dist/handlers/projectMembers.js.map +1 -1
  98. package/dist/handlers/projects.d.ts.map +1 -1
  99. package/dist/handlers/projects.js +10 -3
  100. package/dist/handlers/projects.js.map +1 -1
  101. package/dist/http/api-token.d.ts +17 -0
  102. package/dist/http/api-token.d.ts.map +1 -1
  103. package/dist/http/api-token.js +49 -14
  104. package/dist/http/api-token.js.map +1 -1
  105. package/dist/http/bounded-ttl-cache.d.ts +17 -0
  106. package/dist/http/bounded-ttl-cache.d.ts.map +1 -0
  107. package/dist/http/bounded-ttl-cache.js +58 -0
  108. package/dist/http/bounded-ttl-cache.js.map +1 -0
  109. package/dist/http/csrf.d.ts.map +1 -1
  110. package/dist/http/csrf.js +4 -2
  111. package/dist/http/csrf.js.map +1 -1
  112. package/dist/http/index.d.ts +3 -1
  113. package/dist/http/index.d.ts.map +1 -1
  114. package/dist/http/index.js +3 -1
  115. package/dist/http/index.js.map +1 -1
  116. package/dist/http/redirect.d.ts.map +1 -1
  117. package/dist/http/redirect.js +5 -0
  118. package/dist/http/redirect.js.map +1 -1
  119. package/dist/http/session-refresher.d.ts +14 -0
  120. package/dist/http/session-refresher.d.ts.map +1 -0
  121. package/dist/http/session-refresher.js +34 -0
  122. package/dist/http/session-refresher.js.map +1 -0
  123. package/dist/notifications/smtp-provider.d.ts.map +1 -1
  124. package/dist/notifications/smtp-provider.js +13 -6
  125. package/dist/notifications/smtp-provider.js.map +1 -1
  126. package/dist/oauth/index.d.ts +3 -0
  127. package/dist/oauth/index.d.ts.map +1 -1
  128. package/dist/oauth/index.js +14 -8
  129. package/dist/oauth/index.js.map +1 -1
  130. package/dist/organizations/org-asset-service.d.ts +2 -2
  131. package/dist/organizations/org-asset-service.js +2 -2
  132. package/dist/organizations/user-memberships.d.ts.map +1 -1
  133. package/dist/organizations/user-memberships.js +3 -7
  134. package/dist/organizations/user-memberships.js.map +1 -1
  135. package/dist/projects/delete-with-blobs.d.ts +17 -0
  136. package/dist/projects/delete-with-blobs.d.ts.map +1 -0
  137. package/dist/projects/delete-with-blobs.js +36 -0
  138. package/dist/projects/delete-with-blobs.js.map +1 -0
  139. package/dist/projects/index.d.ts +1 -0
  140. package/dist/projects/index.d.ts.map +1 -1
  141. package/dist/projects/index.js +1 -0
  142. package/dist/projects/index.js.map +1 -1
  143. package/package.json +11 -10
@@ -11,7 +11,7 @@ import { apiError, ApiErrorCode, collection, created, noContent, parseBody, pars
11
11
  import { canChangeVisibilityToPublic, canCreateDefinition, canEdit, canSolve, canView, hasPermission, slugify, SYSTEM_CONTEXT, outsideTokenOrg, scopeAllows, validateProjectFlags, withAdminBypass } from '@selvajs/platform';
12
12
  import { resolveAccessibleProjects } from '../definitions/index.js';
13
13
  import { projectAccessInputFromRowsWith, requireCanCreateProject, requireCanEditProjectSettings, requireCanManage } from '../access/index.js';
14
- import { createProjectWithUniqueSlug } from '../projects/index.js';
14
+ import { createProjectWithUniqueSlug, deleteProjectWithBlobs } from '../projects/index.js';
15
15
  /**
16
16
  * Projects the caller can view. `canCreateDefinitions` says whether
17
17
  * `POST /definitions` would take this project from this caller: the same rule
@@ -160,7 +160,10 @@ export const updateProject = async (req) => {
160
160
  // A renamed project gets a re-derived slug; the two must not drift apart.
161
161
  if (input.name !== undefined) {
162
162
  patch.name = input.name;
163
- patch.slug = slugify(input.name);
163
+ // A name with no usable letters yields '', which keeps the current slug.
164
+ const slug = slugify(input.name);
165
+ if (slug)
166
+ patch.slug = slug;
164
167
  }
165
168
  if (input.description !== undefined)
166
169
  patch.description = input.description ?? undefined;
@@ -175,7 +178,11 @@ export const deleteProject = async (req) => {
175
178
  const { id } = requireParams(req.params, 'id');
176
179
  const { ctx } = requireCaller(req);
177
180
  await requireCanManage(req, id);
178
- await req.deps.projects.deleteProject(ctx, id);
181
+ await deleteProjectWithBlobs({
182
+ projects: req.deps.projects,
183
+ definitions: req.deps.definitionMeta,
184
+ storage: req.deps.storage
185
+ }, ctx, id);
179
186
  return noContent();
180
187
  };
181
188
  //# sourceMappingURL=projects.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"projects.js","sourceRoot":"","sources":["../../src/handlers/projects.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"projects.js","sourceRoot":"","sources":["../../src/handlers/projects.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACN,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,OAAO,EACP,SAAS,EACT,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,uBAAuB,EACvB,uBAAuB,EAEvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACN,2BAA2B,EAC3B,mBAAmB,EACnB,OAAO,EACP,QAAQ,EACR,OAAO,EACP,aAAa,EACb,OAAO,EACP,cAAc,EACd,eAAe,EACf,WAAW,EACX,oBAAoB,EACpB,eAAe,EAEf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EACN,8BAA8B,EAC9B,uBAAuB,EACvB,6BAA6B,EAC7B,gBAAgB,EAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAE3F;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAe,KAAK,EAAE,GAAG,EAAE,EAAE;IACrD,MAAM,EAAE,GAAG,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAEnC,MAAM,GAAG,GAAG,MAAM,yBAAyB,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3D,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC;IACzB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACpD,6EAA6E;IAC7E,4DAA4D;IAC5D,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QAC3C,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;IACjE,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,KAAK,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACxE,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;IACvC,+EAA+E;IAC/E,MAAM,cAAc,GAAG,WAAW,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAClD,OAAO,UAAU,CAAC;QACjB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAC9B,GAAG,OAAO;YACV,oBAAoB,EACnB,cAAc;gBACd,mBAAmB,CAClB,8BAA8B,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE;oBACjD,MAAM,EAAE,GAAG,CAAC,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,IAAI;oBACrD,SAAS,EAAE,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI;oBAC1D,cAAc,EAAE,GAAG,CAAC,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE;iBAC3D,CAAC,CACF;SACF,CAAC,CAAC;QACH,UAAU,EAAE,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS;KACvE,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAe,KAAK,EAAE,GAAG,EAAE,EAAE;IACtD,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,CAAC,GAAG,CAAC,WAAW;QAAE,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,iBAAiB,EAAE,wBAAwB,CAAC,CAAC;IAC9F,qEAAqE;IACrE,mEAAmE;IACnE,MAAM,uBAAuB,CAAC,GAAG,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;IAEpD,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,uBAAuB,CAAC,CAAC;IAEpE,0EAA0E;IAC1E,yEAAyE;IACzE,mEAAmE;IACnE,+BAA+B;IAC/B,IAAI,KAAK,CAAC,UAAU,KAAK,UAAU,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,gBAAgB,CAAC,EAAE,CAAC;QAC9E,QAAQ,CACP,GAAG,EACH,YAAY,CAAC,SAAS,EACtB,sEAAsE,CACtE,CAAC;IACH,CAAC;IAED,MAAM,gBAAgB,GAAG,KAAK,CAAC,gBAAgB,IAAI,KAAK,CAAC;IACzD,sEAAsE;IACtE,6BAA6B;IAC7B,MAAM,UAAU,GAAG,oBAAoB,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAC5F,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,QAAQ,CACP,GAAG,EACH,YAAY,CAAC,iBAAiB,EAC9B,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAC3D,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,2BAA2B,CAChD,GAAG,CAAC,IAAI,CAAC,QAAQ,EACjB;QACC,EAAE,EAAE,UAAU,EAAE;QAChB,KAAK,EAAE,GAAG,CAAC,WAAW;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,OAAO,EAAE,IAAI,CAAC,EAAE;QAChB,SAAS,EAAE,IAAI,CAAC,EAAE;QAClB,SAAS,EAAE,IAAI,CAAC,EAAE;QAClB,gBAAgB;KAChB,EACD,EAAE,QAAQ,EAAE,GAAG,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,mBAAmB,EAAE,CAC9E,CAAC;IACF,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC;AACzB,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,UAAU,GAAe,KAAK,EAAE,GAAG,EAAE,EAAE;IACnD,MAAM,EAAE,EAAE,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC/C,MAAM,EAAE,GAAG,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAEnC,6EAA6E;IAC7E,kEAAkE;IAClE,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IACvE,IAAI,CAAC,OAAO,IAAI,eAAe,CAAC,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACrD,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,CAAC,UAAU,EAAE,cAAc,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC9D,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,cAAc,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC;QAC3E,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC,cAAc,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC;QAChF,OAAO,CAAC,UAAU,KAAK,UAAU;YAChC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,qBAAqB,CAAC,aAAa,CAAC,cAAc,EAAE,OAAO,CAAC,EAAE,CAAC;YAC1E,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;KACtB,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;IACtD,MAAM,KAAK,GAAG,8BAA8B,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE;QAC/D,MAAM;QACN,SAAS,EAAE,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI;QAChD,cAAc,EAAE,MAAM;KACtB,CAAC,CAAC;IACH,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAEhF,OAAO;QACN,IAAI,EAAE;YACL,GAAG,OAAO;YACV,IAAI,EAAE,MAAM,EAAE,IAAI,IAAI,IAAI;YAC1B,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC;YACvB,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC;SACzB;KACD,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAe,KAAK,EAAE,GAAG,EAAE,EAAE;IACtD,MAAM,EAAE,EAAE,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAE/C,4EAA4E;IAC5E,yEAAyE;IACzE,yEAAyE;IACzE,0EAA0E;IAC1E,0EAA0E;IAC1E,sBAAsB;IACtB,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,6BAA6B,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAEhF,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,uBAAuB,CAAC,CAAC;IAEpE,wEAAwE;IACxE,uDAAuD;IACvD,yEAAyE;IACzE,oEAAoE;IACpE,IAAI,KAAK,CAAC,UAAU,KAAK,UAAU,IAAI,QAAQ,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;QAC3E,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,gBAAgB,CAAC,EAAE,CAAC;YAC3C,QAAQ,CACP,GAAG,EACH,YAAY,CAAC,SAAS,EACtB,+DAA+D,CAC/D,CAAC;QACH,CAAC;IACF,CAAC;IAED,uEAAuE;IACvE,eAAe;IACf,IAAI,KAAK,CAAC,UAAU,KAAK,QAAQ,IAAI,QAAQ,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;QACvE,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;QACpF,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,mBAAmB,EAAE,GAAG,EAAE,CAC5D,2BAA2B,CAAC,EAAE,SAAS,EAAE,CAAC,CAC1C,CAAC;QACF,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,SAAS,EAAE,sDAAsD,CAAC,CAAC;QAC/F,CAAC;IACF,CAAC;IAED,0EAA0E;IAC1E,oEAAoE;IACpE,MAAM,UAAU,GAAG,oBAAoB,CAAC;QACvC,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,QAAQ,CAAC,UAAU;QACnD,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,IAAI,QAAQ,CAAC,gBAAgB;KACrE,CAAC,CAAC;IACH,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,QAAQ,CACP,GAAG,EACH,YAAY,CAAC,iBAAiB,EAC9B,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAC3D,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAMP,EAAE,CAAC;IACP,0EAA0E;IAC1E,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACxB,yEAAyE;QACzE,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,IAAI;YAAE,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;IAC7B,CAAC;IACD,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS;QAAE,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,IAAI,SAAS,CAAC;IACxF,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS;QAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;IACxE,IAAI,KAAK,CAAC,gBAAgB,KAAK,SAAS;QAAE,KAAK,CAAC,gBAAgB,GAAG,KAAK,CAAC,gBAAgB,CAAC;IAE1F,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,GAAG,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;IACtD,OAAO,SAAS,EAAE,CAAC;AACpB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAe,KAAK,EAAE,GAAG,EAAE,EAAE;IACtD,MAAM,EAAE,EAAE,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC/C,MAAM,EAAE,GAAG,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,gBAAgB,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAEhC,MAAM,sBAAsB,CAC3B;QACC,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,QAAQ;QAC3B,WAAW,EAAE,GAAG,CAAC,IAAI,CAAC,cAAc;QACpC,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,OAAO;KACzB,EACD,GAAG,EACH,EAAE,CACF,CAAC;IACF,OAAO,SAAS,EAAE,CAAC;AACpB,CAAC,CAAC"}
@@ -31,6 +31,11 @@ export interface ResolveApiTokenDeps {
31
31
  * rule's project scan. Absent: every flag off, which only ever refuses more.
32
32
  */
33
33
  flag?: (name: keyof SelvaFlags) => boolean;
34
+ /**
35
+ * Skips the holder check for a token that passed it within the cache's TTL.
36
+ * Absent: checked on every request.
37
+ */
38
+ holderVerdicts?: HolderVerdictCache;
34
39
  /** Receives background failures (the `lastUsedAt` write). Logs the token id only. */
35
40
  log?: ILogger;
36
41
  now?: () => number;
@@ -52,4 +57,16 @@ export interface ResolveApiTokenOptions {
52
57
  * narrowed to the token's scopes.
53
58
  */
54
59
  export declare function resolveApiToken(request: Request, deps: ResolveApiTokenDeps, { pathPrefix }?: ResolveApiTokenOptions): Promise<ApiTokenResolution>;
60
+ export interface HolderVerdictCache {
61
+ passed(tokenId: string, now: number): boolean;
62
+ remember(tokenId: string, now: number): void;
63
+ }
64
+ /**
65
+ * Remembers passed holder checks for `ttlMs`, so a busy upload key doesn't
66
+ * re-scan its org's projects on every request. A withdrawn right takes effect
67
+ * within the TTL; a disabled owner or a removed member is still refused at once.
68
+ */
69
+ export declare function createHolderVerdictCache(ttlMs?: number, maxEntries?: number): HolderVerdictCache;
70
+ /** The bearer credential, or null. CSRF's exemption reads it here too: the two must agree. */
71
+ export declare function bearerOf(request: Request): string | null;
55
72
  //# sourceMappingURL=api-token.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"api-token.d.ts","sourceRoot":"","sources":["../../src/http/api-token.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,QAAQ,EACR,QAAQ,EACR,aAAa,EACb,aAAa,EACb,OAAO,EACP,cAAc,EACd,UAAU,EACV,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAGN,KAAK,aAAa,EAClB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAoB,KAAK,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAG5F,MAAM,MAAM,kBAAkB;AAC7B,2EAA2E;AACzE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE;AACtE,0EAA0E;GACxE;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7E,MAAM,WAAW,mBAAmB;IACnC,8EAA8E;IAC9E,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,GAAG,kBAAkB,CAAC,CAAC;IAC1D,IAAI,EAAE,IAAI,CACT,aAAa,EACb,WAAW,GAAG,MAAM,GAAG,aAAa,GAAG,UAAU,GAAG,uBAAuB,GAAG,aAAa,CAC3F,CAAC;IACF,qHAAqH;IACrH,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;IACxC;;;OAGG;IACH,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,UAAU,KAAK,OAAO,CAAC;IAC3C,qFAAqF;IACrF,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,sBAAsB;IACtC,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AA0BD;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CACpC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,mBAAmB,EACzB,EAAE,UAAuB,EAAE,GAAE,sBAA2B,GACtD,OAAO,CAAC,kBAAkB,CAAC,CAiF7B"}
1
+ {"version":3,"file":"api-token.d.ts","sourceRoot":"","sources":["../../src/http/api-token.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,QAAQ,EACR,QAAQ,EACR,aAAa,EACb,aAAa,EACb,OAAO,EACP,cAAc,EACd,UAAU,EACV,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAGN,KAAK,aAAa,EAClB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAoB,KAAK,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAG5F,MAAM,MAAM,kBAAkB;AAC7B,2EAA2E;AACzE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE;AACtE,0EAA0E;GACxE;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7E,MAAM,WAAW,mBAAmB;IACnC,8EAA8E;IAC9E,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,GAAG,kBAAkB,CAAC,CAAC;IAC1D,IAAI,EAAE,IAAI,CACT,aAAa,EACb,WAAW,GAAG,MAAM,GAAG,aAAa,GAAG,UAAU,GAAG,uBAAuB,GAAG,aAAa,CAC3F,CAAC;IACF,qHAAqH;IACrH,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;IACxC;;;OAGG;IACH,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,UAAU,KAAK,OAAO,CAAC;IAC3C;;;OAGG;IACH,cAAc,CAAC,EAAE,kBAAkB,CAAC;IACpC,qFAAqF;IACrF,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,sBAAsB;IACtC,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AA0BD;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CACpC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,mBAAmB,EACzB,EAAE,UAAuB,EAAE,GAAE,sBAA2B,GACtD,OAAO,CAAC,kBAAkB,CAAC,CAoF7B;AAED,MAAM,WAAW,kBAAkB;IAClC,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7C;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,SAAS,EAAE,UAAU,SAAS,GAAG,kBAAkB,CAoBhG;AAED,8FAA8F;AAC9F,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAKxD"}
@@ -79,18 +79,22 @@ export async function resolveApiToken(request, deps, { pathPrefix = '/api/v1/' }
79
79
  if (!user || user.disabled || !member)
80
80
  return UNAUTHORIZED;
81
81
  const live = await buildRequestContext({ user, platformPermissions, membership: member, pinOrg: true }, { orgs: deps.data.orgs });
82
- const holds = await canHoldApiTokens(live, token.scopes, {
83
- policy: deps.mayHoldApiTokens,
84
- deps: {
85
- orgs: deps.data.orgs,
86
- projects: deps.data.projects,
87
- platformProjectGrants: deps.data.platformProjectGrants,
88
- definitionMeta: deps.data.definitions,
89
- flag: deps.flag ?? (() => false)
90
- }
91
- });
92
- if (!holds)
93
- return HOLDER_REFUSED;
82
+ const now = deps.now?.() ?? Date.now();
83
+ if (!deps.holderVerdicts?.passed(token.id, now)) {
84
+ const holds = await canHoldApiTokens(live, token.scopes, {
85
+ policy: deps.mayHoldApiTokens,
86
+ deps: {
87
+ orgs: deps.data.orgs,
88
+ projects: deps.data.projects,
89
+ platformProjectGrants: deps.data.platformProjectGrants,
90
+ definitionMeta: deps.data.definitions,
91
+ flag: deps.flag ?? (() => false)
92
+ }
93
+ });
94
+ if (!holds)
95
+ return HOLDER_REFUSED;
96
+ deps.holderVerdicts?.remember(token.id, now);
97
+ }
94
98
  let ctx = narrowApiTokenContext(live, token);
95
99
  // Signed only after the user check above: a minted session can't be
96
100
  // revoked, and row security doesn't look at bans.
@@ -98,7 +102,6 @@ export async function resolveApiToken(request, deps, { pathPrefix = '/api/v1/' }
98
102
  const sessionToken = await delegated.mint(user.id, { orgId: token.orgId });
99
103
  ctx = { ...ctx, adapterContext: { sessionToken } };
100
104
  }
101
- const now = deps.now?.() ?? Date.now();
102
105
  if (!token.lastUsedAt || now - Date.parse(token.lastUsedAt) >= LAST_USED_WRITE_INTERVAL_MS) {
103
106
  store.touchLastUsed(SYSTEM_CONTEXT, token.id, new Date(now).toISOString()).catch((err) => {
104
107
  deps.log?.warn('Failed to record API token use', {
@@ -110,7 +113,39 @@ export async function resolveApiToken(request, deps, { pathPrefix = '/api/v1/' }
110
113
  }
111
114
  return { kind: 'ok', user, ctx, token };
112
115
  }
113
- function bearerOf(request) {
116
+ /**
117
+ * Remembers passed holder checks for `ttlMs`, so a busy upload key doesn't
118
+ * re-scan its org's projects on every request. A withdrawn right takes effect
119
+ * within the TTL; a disabled owner or a removed member is still refused at once.
120
+ */
121
+ export function createHolderVerdictCache(ttlMs = 30_000, maxEntries = 10_000) {
122
+ const until = new Map();
123
+ return {
124
+ passed(tokenId, now) {
125
+ const expiry = until.get(tokenId);
126
+ if (expiry === undefined)
127
+ return false;
128
+ if (expiry > now)
129
+ return true;
130
+ until.delete(tokenId);
131
+ return false;
132
+ },
133
+ remember(tokenId, now) {
134
+ if (until.size >= maxEntries) {
135
+ for (const [id, expiry] of until)
136
+ if (expiry <= now)
137
+ until.delete(id);
138
+ // Still full of live entries: drop the oldest insert.
139
+ if (until.size >= maxEntries)
140
+ until.delete(until.keys().next().value);
141
+ }
142
+ until.delete(tokenId);
143
+ until.set(tokenId, now + ttlMs);
144
+ }
145
+ };
146
+ }
147
+ /** The bearer credential, or null. CSRF's exemption reads it here too: the two must agree. */
148
+ export function bearerOf(request) {
114
149
  const header = request.headers.get('authorization');
115
150
  if (!header)
116
151
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"api-token.js","sourceRoot":"","sources":["../../src/http/api-token.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api-token.js","sourceRoot":"","sources":["../../src/http/api-token.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export interface BoundedTtlCacheConfig {
2
+ /** Max retained entries. Once full, expired entries go first, then oldest-inserted. */
3
+ maxEntries: number;
4
+ }
5
+ export interface BoundedTtlCache<T> {
6
+ /** The live value, or `undefined` when absent or expired. Expired entries are dropped on read. */
7
+ get(key: string): T | undefined;
8
+ /** Store `value` under `key`, expiring `ttlMs` from now. Re-inserting a key refreshes its insertion order. */
9
+ set(key: string, value: T, ttlMs: number): void;
10
+ delete(key: string): void;
11
+ /** Test seam and force-refresh gestures. */
12
+ clear(): void;
13
+ /** Retained entries, including any expired but not yet swept. For tests/observability. */
14
+ size(): number;
15
+ }
16
+ export declare function createBoundedTtlCache<T>(config: BoundedTtlCacheConfig): BoundedTtlCache<T>;
17
+ //# sourceMappingURL=bounded-ttl-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounded-ttl-cache.d.ts","sourceRoot":"","sources":["../../src/http/bounded-ttl-cache.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,qBAAqB;IACrC,uFAAuF;IACvF,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe,CAAC,CAAC;IACjC,kGAAkG;IAClG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;IAChC,8GAA8G;IAC9G,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,4CAA4C;IAC5C,KAAK,IAAI,IAAI,CAAC;IACd,0FAA0F;IAC1F,IAAI,IAAI,MAAM,CAAC;CACf;AAOD,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,MAAM,EAAE,qBAAqB,GAAG,eAAe,CAAC,CAAC,CAAC,CA6C1F"}
@@ -0,0 +1,58 @@
1
+ // Process-local TTL cache with a hard entry cap.
2
+ //
3
+ // Sweeping is amortized onto `set` rather than driven by a timer: a timer would
4
+ // keep the event loop alive and give this module a lifecycle its callers — plain
5
+ // module-level singletons — do not have.
6
+ //
7
+ // Values are stored as-is. A caller that caches failures alongside successes
8
+ // encodes both in ONE value type and gives each its own TTL at the `set` call,
9
+ // rather than this holding a success/error union it would have to interpret.
10
+ export function createBoundedTtlCache(config) {
11
+ const maxEntries = Math.max(1, Math.floor(config.maxEntries));
12
+ const entries = new Map();
13
+ function evictIfFull(now) {
14
+ if (entries.size < maxEntries)
15
+ return;
16
+ for (const [key, entry] of entries) {
17
+ if (entry.expiresAt <= now)
18
+ entries.delete(key);
19
+ }
20
+ // Map preserves insertion order, so the first key is the oldest-inserted.
21
+ while (entries.size >= maxEntries) {
22
+ const oldest = entries.keys().next().value;
23
+ if (oldest === undefined)
24
+ break;
25
+ entries.delete(oldest);
26
+ }
27
+ }
28
+ return {
29
+ get(key) {
30
+ const entry = entries.get(key);
31
+ if (!entry)
32
+ return undefined;
33
+ if (entry.expiresAt <= Date.now()) {
34
+ entries.delete(key);
35
+ return undefined;
36
+ }
37
+ return entry.value;
38
+ },
39
+ set(key, value, ttlMs) {
40
+ // Delete first so a re-insert moves the key to the end of the insertion
41
+ // order — otherwise a hot key keeps its original position and is evicted
42
+ // while colder, newer keys survive.
43
+ entries.delete(key);
44
+ evictIfFull(Date.now());
45
+ entries.set(key, { value, expiresAt: Date.now() + ttlMs });
46
+ },
47
+ delete(key) {
48
+ entries.delete(key);
49
+ },
50
+ clear() {
51
+ entries.clear();
52
+ },
53
+ size() {
54
+ return entries.size;
55
+ }
56
+ };
57
+ }
58
+ //# sourceMappingURL=bounded-ttl-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounded-ttl-cache.js","sourceRoot":"","sources":["../../src/http/bounded-ttl-cache.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,EAAE;AACF,gFAAgF;AAChF,iFAAiF;AACjF,yCAAyC;AACzC,EAAE;AACF,6EAA6E;AAC7E,+EAA+E;AAC/E,6EAA6E;AAwB7E,MAAM,UAAU,qBAAqB,CAAI,MAA6B;IACrE,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IAC9D,MAAM,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAC;IAE5C,SAAS,WAAW,CAAC,GAAW;QAC/B,IAAI,OAAO,CAAC,IAAI,GAAG,UAAU;YAAE,OAAO;QACtC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;YACpC,IAAI,KAAK,CAAC,SAAS,IAAI,GAAG;gBAAE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjD,CAAC;QACD,0EAA0E;QAC1E,OAAO,OAAO,CAAC,IAAI,IAAI,UAAU,EAAE,CAAC;YACnC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;YAC3C,IAAI,MAAM,KAAK,SAAS;gBAAE,MAAM;YAChC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACxB,CAAC;IACF,CAAC;IAED,OAAO;QACN,GAAG,CAAC,GAAG;YACN,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC/B,IAAI,CAAC,KAAK;gBAAE,OAAO,SAAS,CAAC;YAC7B,IAAI,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;gBACnC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACpB,OAAO,SAAS,CAAC;YAClB,CAAC;YACD,OAAO,KAAK,CAAC,KAAK,CAAC;QACpB,CAAC;QACD,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK;YACpB,wEAAwE;YACxE,yEAAyE;YACzE,oCAAoC;YACpC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACpB,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;YACxB,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;QAC5D,CAAC;QACD,MAAM,CAAC,GAAG;YACT,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC;QACD,KAAK;YACJ,OAAO,CAAC,KAAK,EAAE,CAAC;QACjB,CAAC;QACD,IAAI;YACH,OAAO,OAAO,CAAC,IAAI,CAAC;QACrB,CAAC;KACD,CAAC;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"csrf.d.ts","sourceRoot":"","sources":["../../src/http/csrf.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAcH,MAAM,WAAW,WAAW;IAC3B,sDAAsD;IACtD,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAS9F"}
1
+ {"version":3,"file":"csrf.d.ts","sourceRoot":"","sources":["../../src/http/csrf.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAeH,MAAM,WAAW,WAAW;IAC3B,sDAAsD;IACtD,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAU9F"}
package/dist/http/csrf.js CHANGED
@@ -11,6 +11,7 @@
11
11
  * `Origin` (the OAuth token endpoint, per RFC 6749).
12
12
  */
13
13
  import { API_TOKEN_PREFIX } from '../tokens/index.js';
14
+ import { bearerOf } from './api-token.js';
14
15
  const UNSAFE_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);
15
16
  // The content types a browser form can send without a CORS preflight, plus
16
17
  // SvelteKit's own binary form encoding.
@@ -26,8 +27,9 @@ export function isCrossSiteFormSubmission(request, options = {}) {
26
27
  const type = (request.headers.get('content-type') ?? '').split(';')[0].trim().toLowerCase();
27
28
  if (!FORM_TYPES.includes(type))
28
29
  return false;
29
- const bearer = /^Bearer\s+(\S+)/i.exec(request.headers.get('authorization') ?? '')?.[1];
30
- if (bearer?.startsWith(API_TOKEN_PREFIX))
30
+ // The same parse `resolveApiToken` uses: a header one read as a key and the
31
+ // other didn't would skip this check and still run as the cookie session.
32
+ if (bearerOf(request)?.startsWith(API_TOKEN_PREFIX))
31
33
  return false;
32
34
  const url = new URL(request.url);
33
35
  if (options.exemptPaths?.includes(url.pathname))
@@ -1 +1 @@
1
- {"version":3,"file":"csrf.js","sourceRoot":"","sources":["../../src/http/csrf.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AACnE,2EAA2E;AAC3E,wCAAwC;AACxC,MAAM,UAAU,GAAG;IAClB,mCAAmC;IACnC,qBAAqB;IACrB,YAAY;IACZ,kCAAkC;CAClC,CAAC;AAOF,MAAM,UAAU,yBAAyB,CAAC,OAAgB,EAAE,UAAuB,EAAE;IACpF,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC5F,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7C,MAAM,MAAM,GAAG,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACxF,IAAI,MAAM,EAAE,UAAU,CAAC,gBAAgB,CAAC;QAAE,OAAO,KAAK,CAAC;IACvD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9D,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,GAAG,CAAC,MAAM,CAAC;AACrD,CAAC"}
1
+ {"version":3,"file":"csrf.js","sourceRoot":"","sources":["../../src/http/csrf.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AACnE,2EAA2E;AAC3E,wCAAwC;AACxC,MAAM,UAAU,GAAG;IAClB,mCAAmC;IACnC,qBAAqB;IACrB,YAAY;IACZ,kCAAkC;CAClC,CAAC;AAOF,MAAM,UAAU,yBAAyB,CAAC,OAAgB,EAAE,UAAuB,EAAE;IACpF,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC5F,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7C,4EAA4E;IAC5E,0EAA0E;IAC1E,IAAI,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,gBAAgB,CAAC;QAAE,OAAO,KAAK,CAAC;IAClE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9D,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,GAAG,CAAC,MAAM,CAAC;AACrD,CAAC"}
@@ -5,5 +5,7 @@ export { applySecurityHeaders, type SecurityHeaderOptions } from './security-hea
5
5
  export { createRouteClassifier, type RouteClassifier, type RouteClassifierConfig } from './route-classifier.js';
6
6
  export { createLoginRateLimiter, addressKeysCollapsed, type LoginRateLimiter, type LoginRateLimiterConfig } from './login-rate-limit.js';
7
7
  export { buildRequestContext, type BuildRequestContextInput } from './request-context.js';
8
- export { resolveApiToken, type ApiTokenResolution, type ResolveApiTokenDeps, type ResolveApiTokenOptions } from './api-token.js';
8
+ export { createBoundedTtlCache, type BoundedTtlCache, type BoundedTtlCacheConfig } from './bounded-ttl-cache.js';
9
+ export { createSessionRefresher, type SessionRefresher } from './session-refresher.js';
10
+ export { createHolderVerdictCache, resolveApiToken, type ApiTokenResolution, type HolderVerdictCache, type ResolveApiTokenDeps, type ResolveApiTokenOptions } from './api-token.js';
9
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,yBAAyB,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EAAE,uBAAuB,EAAE,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAAE,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACzF,OAAO,EACN,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EACpB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EACN,eAAe,EACf,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,yBAAyB,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EAAE,uBAAuB,EAAE,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAAE,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACzF,OAAO,EACN,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EACpB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EACN,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,sBAAsB,EAAE,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACvF,OAAO,EACN,wBAAwB,EACxB,eAAe,EACf,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,MAAM,gBAAgB,CAAC"}
@@ -6,5 +6,7 @@ export { applySecurityHeaders } from './security-headers.js';
6
6
  export { createRouteClassifier } from './route-classifier.js';
7
7
  export { createLoginRateLimiter, addressKeysCollapsed } from './login-rate-limit.js';
8
8
  export { buildRequestContext } from './request-context.js';
9
- export { resolveApiToken } from './api-token.js';
9
+ export { createBoundedTtlCache } from './bounded-ttl-cache.js';
10
+ export { createSessionRefresher } from './session-refresher.js';
11
+ export { createHolderVerdictCache, resolveApiToken } from './api-token.js';
10
12
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAEvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,yBAAyB,EAAoB,MAAM,WAAW,CAAC;AACxE,OAAO,EAAE,uBAAuB,EAAoB,MAAM,gBAAgB,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAA8B,MAAM,uBAAuB,CAAC;AACzF,OAAO,EACN,qBAAqB,EAGrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EAGpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,mBAAmB,EAAiC,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EACN,eAAe,EAIf,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAEvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,yBAAyB,EAAoB,MAAM,WAAW,CAAC;AACxE,OAAO,EAAE,uBAAuB,EAAoB,MAAM,gBAAgB,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAA8B,MAAM,uBAAuB,CAAC;AACzF,OAAO,EACN,qBAAqB,EAGrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EAGpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,mBAAmB,EAAiC,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EACN,qBAAqB,EAGrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,sBAAsB,EAAyB,MAAM,wBAAwB,CAAC;AACvF,OAAO,EACN,wBAAwB,EACxB,eAAe,EAKf,MAAM,gBAAgB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"redirect.d.ts","sourceRoot":"","sources":["../../src/http/redirect.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAU3F"}
1
+ {"version":3,"file":"redirect.d.ts","sourceRoot":"","sources":["../../src/http/redirect.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAc3F"}
@@ -11,6 +11,11 @@ export function safeRedirectTarget(raw, fallback) {
11
11
  // Absolute URLs (`https://evil.com`) leave this origin.
12
12
  if (raw[0] !== '/')
13
13
  return fallback;
14
+ // Browsers drop tab, CR and LF anywhere in a URL, so `/\t/evil.com` lands
15
+ // as `//evil.com` after the check below has passed it.
16
+ // eslint-disable-next-line no-control-regex -- matching control characters is the point
17
+ if (/[\u0000-\u001f\u007f]/.test(raw))
18
+ return fallback;
14
19
  // `//evil.com` is protocol-relative — a browser treats it as cross-origin —
15
20
  // and some browsers normalize `/\evil.com` into the same thing.
16
21
  if (raw[1] === '/' || raw[1] === '\\')
@@ -1 +1 @@
1
- {"version":3,"file":"redirect.js","sourceRoot":"","sources":["../../src/http/redirect.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAA8B,EAAE,QAAgB;IAClF,4EAA4E;IAC5E,gDAAgD;IAChD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC/D,wDAAwD;IACxD,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG;QAAE,OAAO,QAAQ,CAAC;IACpC,4EAA4E;IAC5E,gEAAgE;IAChE,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI;QAAE,OAAO,QAAQ,CAAC;IACvD,OAAO,GAAG,CAAC;AACZ,CAAC"}
1
+ {"version":3,"file":"redirect.js","sourceRoot":"","sources":["../../src/http/redirect.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAA8B,EAAE,QAAgB;IAClF,4EAA4E;IAC5E,gDAAgD;IAChD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC/D,wDAAwD;IACxD,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG;QAAE,OAAO,QAAQ,CAAC;IACpC,0EAA0E;IAC1E,uDAAuD;IACvD,wFAAwF;IACxF,IAAI,uBAAuB,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IACvD,4EAA4E;IAC5E,gEAAgE;IAChE,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI;QAAE,OAAO,QAAQ,CAAC;IACvD,OAAO,GAAG,CAAC;AACZ,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { ISessionRefresh } from '@selvajs/platform';
2
+ type SessionPair = NonNullable<Awaited<ReturnType<ISessionRefresh['refreshSession']>>>;
3
+ export type SessionRefresher = (refreshToken: string) => Promise<SessionPair | null>;
4
+ /**
5
+ * A page's parallel requests all arrive with the same expired session and the same refresh token,
6
+ * and a provider may rotate that token on first use. Each request exchanging it alone would see
7
+ * every one after the first refused, read that as a dead session, and clear the cookie: a signed-in
8
+ * user logged out by loading a page. So they share one exchange and its answer.
9
+ *
10
+ * `getRefresh` is read per exchange so a host can swap providers. One per process.
11
+ */
12
+ export declare function createSessionRefresher(getRefresh: () => ISessionRefresh | undefined): SessionRefresher;
13
+ export {};
14
+ //# sourceMappingURL=session-refresher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-refresher.d.ts","sourceRoot":"","sources":["../../src/http/session-refresher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGzD,KAAK,WAAW,GAAG,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;AAMvF,MAAM,MAAM,gBAAgB,GAAG,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;AAErF;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACrC,UAAU,EAAE,MAAM,eAAe,GAAG,SAAS,GAC3C,gBAAgB,CAoBlB"}
@@ -0,0 +1,34 @@
1
+ import { createBoundedTtlCache } from './bounded-ttl-cache.js';
2
+ /** Long enough for the requests of one page load that left before the new cookie landed. */
3
+ const RECENT_PAIR_TTL_MS = 10_000;
4
+ const RECENT_PAIR_MAX = 1_000;
5
+ /**
6
+ * A page's parallel requests all arrive with the same expired session and the same refresh token,
7
+ * and a provider may rotate that token on first use. Each request exchanging it alone would see
8
+ * every one after the first refused, read that as a dead session, and clear the cookie: a signed-in
9
+ * user logged out by loading a page. So they share one exchange and its answer.
10
+ *
11
+ * `getRefresh` is read per exchange so a host can swap providers. One per process.
12
+ */
13
+ export function createSessionRefresher(getRefresh) {
14
+ const inflight = new Map();
15
+ const recent = createBoundedTtlCache({ maxEntries: RECENT_PAIR_MAX });
16
+ async function exchange(refreshToken) {
17
+ const pair = (await getRefresh()?.refreshSession(refreshToken)) ?? null;
18
+ if (pair)
19
+ recent.set(refreshToken, pair, RECENT_PAIR_TTL_MS);
20
+ return pair;
21
+ }
22
+ return (refreshToken) => {
23
+ const done = recent.get(refreshToken);
24
+ if (done)
25
+ return Promise.resolve(done);
26
+ let pending = inflight.get(refreshToken);
27
+ if (!pending) {
28
+ pending = exchange(refreshToken).finally(() => inflight.delete(refreshToken));
29
+ inflight.set(refreshToken, pending);
30
+ }
31
+ return pending;
32
+ };
33
+ }
34
+ //# sourceMappingURL=session-refresher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-refresher.js","sourceRoot":"","sources":["../../src/http/session-refresher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAI/D,4FAA4F;AAC5F,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAClC,MAAM,eAAe,GAAG,KAAK,CAAC;AAI9B;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CACrC,UAA6C;IAE7C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAuC,CAAC;IAChE,MAAM,MAAM,GAAG,qBAAqB,CAAc,EAAE,UAAU,EAAE,eAAe,EAAE,CAAC,CAAC;IAEnF,KAAK,UAAU,QAAQ,CAAC,YAAoB;QAC3C,MAAM,IAAI,GAAG,CAAC,MAAM,UAAU,EAAE,EAAE,cAAc,CAAC,YAAY,CAAC,CAAC,IAAI,IAAI,CAAC;QACxE,IAAI,IAAI;YAAE,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,kBAAkB,CAAC,CAAC;QAC7D,OAAO,IAAI,CAAC;IACb,CAAC;IAED,OAAO,CAAC,YAAY,EAAE,EAAE;QACvB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACtC,IAAI,IAAI;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACzC,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,OAAO,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;YAC9E,QAAQ,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACrC,CAAC;QACD,OAAO,OAAO,CAAC;IAChB,CAAC,CAAC;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"smtp-provider.d.ts","sourceRoot":"","sources":["../../src/notifications/smtp-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,OAAO,EACZ,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAkB,KAAK,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhE;;;;;;;GAOG;AACH,qBAAa,wBAAyB,YAAW,qBAAqB;IAGzD,OAAO,CAAC,QAAQ,CAAC,GAAG;IAFhC,QAAQ,CAAC,IAAI,UAAU;gBAEM,GAAG,EAAE,OAAO;IAEnC,IAAI,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,GAAE,OAA0B,GAAG,OAAO,CAAC,UAAU,CAAC;IA6C1F,YAAY,IAAI,OAAO;CAOvB"}
1
+ {"version":3,"file":"smtp-provider.d.ts","sourceRoot":"","sources":["../../src/notifications/smtp-provider.ts"],"names":[],"mappings":"AACA,OAAO,EAEN,KAAK,OAAO,EACZ,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAkB,KAAK,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAShE;;;;;;;GAOG;AACH,qBAAa,wBAAyB,YAAW,qBAAqB;IAGzD,OAAO,CAAC,QAAQ,CAAC,GAAG;IAFhC,QAAQ,CAAC,IAAI,UAAU;gBAEM,GAAG,EAAE,OAAO;IAEnC,IAAI,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,GAAE,OAA0B,GAAG,OAAO,CAAC,UAAU,CAAC;IA8C1F,YAAY,IAAI,OAAO;CAOvB"}
@@ -1,5 +1,11 @@
1
+ import { createHmac, randomBytes } from 'node:crypto';
1
2
  import { NoopLogger } from '@selvajs/platform';
2
3
  import { readSmtpConfig } from './smtp-config.js';
4
+ // Per process: the hash only has to match up within one run of the logs.
5
+ const RECIPIENT_HASH_KEY = randomBytes(32);
6
+ function recipientHash(to) {
7
+ return createHmac('sha256', RECIPIENT_HASH_KEY).update(to).digest('hex').slice(0, 16);
8
+ }
3
9
  /**
4
10
  * SMTP transport. Any server works — a transactional provider, an internal
5
11
  * relay, or a mailbox with an app password.
@@ -47,16 +53,17 @@ export class SmtpNotificationProvider {
47
53
  return { status: 'sent' };
48
54
  }
49
55
  catch (err) {
50
- const reason = err instanceof Error ? err.message : String(err);
51
- // Recipient is personal data — logged at warn only, as the minimum
52
- // needed to diagnose a bounce. Never the subject or body.
56
+ // Never the raw message: an SMTP rejection quotes the address, and erasure
57
+ // can't reach stdout. The keyed hash still matches up failures to one person.
58
+ const { code, responseCode } = err;
53
59
  log.warn('Failed to send mail', {
54
60
  component: 'SmtpNotificationProvider',
55
- to: message.to,
61
+ recipient: recipientHash(message.to),
56
62
  kind: message.kind,
57
- reason
63
+ code,
64
+ responseCode
58
65
  });
59
- return { status: 'failed', reason };
66
+ return { status: 'failed', reason: code ?? 'send-failed' };
60
67
  }
61
68
  }
62
69
  isConfigured() {
@@ -1 +1 @@
1
- {"version":3,"file":"smtp-provider.js","sourceRoot":"","sources":["../../src/notifications/smtp-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,UAAU,EAKV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAgB,MAAM,kBAAkB,CAAC;AAEhE;;;;;;;GAOG;AACH,MAAM,OAAO,wBAAwB;IAGP;IAFpB,IAAI,GAAG,MAAM,CAAC;IAEvB,YAA6B,GAAY;QAAZ,QAAG,GAAH,GAAG,CAAS;IAAG,CAAC;IAE7C,KAAK,CAAC,IAAI,CAAC,OAAwB,EAAE,MAAe,IAAI,UAAU,EAAE;QACnE,IAAI,MAAM,CAAC;QACX,IAAI,CAAC;YACJ,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,GAAG,CAAC,KAAK,CAAC,uBAAuB,EAAE;gBAClC,SAAS,EAAE,0BAA0B;gBACrC,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aACvD,CAAC,CAAC;YACH,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;QACtD,CAAC;QACD,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;QAEjD,IAAI,CAAC;YACJ,qEAAqE;YACrE,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;YACvD,MAAM,SAAS,GAAG,eAAe,CAAC;gBACjC,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,IAAI,EAAE,MAAM,CAAC,IAAI;aACjB,CAAC,CAAC;YAEH,MAAM,SAAS,CAAC,QAAQ,CAAC;gBACxB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,IAAI,EAAE,OAAO,CAAC,IAAI;aAClB,CAAC,CAAC;YACH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAC3B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAChE,mEAAmE;YACnE,0DAA0D;YAC1D,GAAG,CAAC,IAAI,CAAC,qBAAqB,EAAE;gBAC/B,SAAS,EAAE,0BAA0B;gBACrC,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,MAAM;aACN,CAAC,CAAC;YACH,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;QACrC,CAAC;IACF,CAAC;IAED,YAAY;QACX,IAAI,CAAC;YACJ,OAAO,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,KAAK,CAAC;QACd,CAAC;IACF,CAAC;CACD"}
1
+ {"version":3,"file":"smtp-provider.js","sourceRoot":"","sources":["../../src/notifications/smtp-provider.ts"],"names":[],"mappings":"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"}
@@ -25,6 +25,7 @@ export declare const REGISTERED_CLIENT_SCOPES: readonly ["read", "solve"];
25
25
  /** The Grasshopper plugin's client id. Built in, so it needs no registration and works air-gapped. */
26
26
  export declare const GRASSHOPPER_CLIENT_ID = "selva-grasshopper";
27
27
  export declare const MCP_PATH = "/mcp";
28
+ export declare const CODE_TTL_MS = 60000;
28
29
  export declare class OAuthError extends Error {
29
30
  /** RFC 6749 / 7591 error code. */
30
31
  readonly error: string;
@@ -78,6 +79,8 @@ export interface OAuthServerOptions {
78
79
  * rebuilt server (a new origin, a rotated secret) still refuses a replay.
79
80
  */
80
81
  redeemedCodes?: Map<string, number>;
82
+ /** Called once a code is exchanged, with the token its grant carries. */
83
+ onRedeemed?: (tokenId: string) => void;
81
84
  now?: () => number;
82
85
  }
83
86
  interface HttpResult {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/oauth/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/oauth/index.ts"],"names":[],"mappings":"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"}
@@ -34,7 +34,7 @@ const GRASSHOPPER_CLIENT = {
34
34
  scopes: ['read', 'upload']
35
35
  };
36
36
  export const MCP_PATH = '/mcp';
37
- const CODE_TTL_MS = 60_000;
37
+ export const CODE_TTL_MS = 60_000;
38
38
  const CLIENT_NAME_MAX = 100;
39
39
  const REDIRECT_URIS_MAX = 10;
40
40
  const CIMD_TTL_MS = 60 * 60_000;
@@ -267,6 +267,7 @@ export function createOAuthServer(options) {
267
267
  throw new OAuthError('invalid_grant', 'The code was already used.');
268
268
  }
269
269
  const { g } = sealed;
270
+ options.onRedeemed?.(g.tokenId);
270
271
  return {
271
272
  status: 200,
272
273
  body: {
@@ -340,16 +341,18 @@ async function readMetadata(url, fetchClientMetadata) {
340
341
  /**
341
342
  * What the request asks for, within what its client may have. A scope outside
342
343
  * that is dropped, not refused, so a client that asks for every advertised
343
- * scope still signs in with what it may have. Asking for none of them means all
344
- * of them. `read` rides along with `solve`, which the consent page offers as
344
+ * scope still signs in with what it may have. No `scope` at all means all of
345
+ * them. Asking only for scopes it may not have gets `read`, never more than it
346
+ * asked for. `read` rides along with `solve`, which the consent page offers as
345
347
  * "read and run solves".
346
348
  */
347
349
  function parseScopes(raw, allowed) {
348
- const asked = (raw ?? '')
349
- .split(' ')
350
- .filter((s) => allowed.includes(s));
351
- if (!asked.length)
350
+ const requested = (raw ?? '').split(' ').filter(Boolean);
351
+ if (!requested.length)
352
352
  return [...allowed];
353
+ const asked = requested.filter((s) => allowed.includes(s));
354
+ if (!asked.length)
355
+ return allowed.includes('read') ? ['read'] : [];
353
356
  return allowed.filter((s) => asked.includes(s) || (s === 'read' && asked.includes('solve')));
354
357
  }
355
358
  /**
@@ -442,7 +445,10 @@ function seal(key, value) {
442
445
  function open(key, sealed) {
443
446
  try {
444
447
  const raw = Buffer.from(sealed, 'base64url');
445
- const decipher = createDecipheriv('aes-256-gcm', key, raw.subarray(0, 12));
448
+ // Without the length, a short code is checked against however many tag bytes it has.
449
+ const decipher = createDecipheriv('aes-256-gcm', key, raw.subarray(0, 12), {
450
+ authTagLength: 16
451
+ });
446
452
  decipher.setAuthTag(raw.subarray(12, 28));
447
453
  const body = Buffer.concat([decipher.update(raw.subarray(28)), decipher.final()]);
448
454
  return JSON.parse(body.toString('utf8'));