@holo-js/auth-clerk 0.3.16 → 0.3.17

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 (2) hide show
  1. package/dist/index.mjs +31 -219
  2. package/package.json +3 -3
package/dist/index.mjs CHANGED
@@ -58,7 +58,6 @@ function normalizeClerkRequest(input) {
58
58
  // src/index.ts
59
59
  var CLERK_RUNTIME_STATE_KEY = "__holoJsAuthClerkRuntime";
60
60
  var clerkDefaultProviderRuntimeCache = /* @__PURE__ */ new Map();
61
- var clerkIdentitySyncLocks = /* @__PURE__ */ new Map();
62
61
  var AUTH_PROVIDER_MARKER = /* @__PURE__ */ Symbol.for("holo-js.auth.provider");
63
62
  function getRuntimeState() {
64
63
  const runtimeGlobal = globalThis;
@@ -345,19 +344,6 @@ function resolveGuardAndProvider(provider) {
345
344
  adapter
346
345
  };
347
346
  }
348
- function requireUserId(adapter, user, message) {
349
- const userId = adapter.getId(user);
350
- if (typeof userId !== "string" && typeof userId !== "number") {
351
- throw new Error(message);
352
- }
353
- return userId;
354
- }
355
- function requireUserRecord(user, message) {
356
- if (!user || typeof user !== "object") {
357
- throw new Error(message);
358
- }
359
- return user;
360
- }
361
347
  function createClerkSessionPayload(authenticated, session) {
362
348
  const user = authenticated.user;
363
349
  return Object.freeze({
@@ -485,56 +471,6 @@ function normalizeHostedProfile(profile) {
485
471
  raw: profile.raw
486
472
  });
487
473
  }
488
- async function findUserByEmail(adapter, email) {
489
- const user = await adapter.findByCredentials({ email: email.trim() });
490
- return user ? requireUserRecord(user, "[@holo-js/auth-clerk] Auth provider lookups must return object users.") : null;
491
- }
492
- async function updateLocalUser(adapter, user, input) {
493
- const current = user;
494
- const changed = Object.entries(input).some(([key, value]) => {
495
- if (key === "email_verified_at") {
496
- return value instanceof Date && !current.email_verified_at;
497
- }
498
- return !Object.is(value, user[key]);
499
- });
500
- if (!changed) {
501
- return { user, changed: false };
502
- }
503
- if (adapter.update) {
504
- return {
505
- user: requireUserRecord(
506
- await adapter.update(user, input),
507
- "[@holo-js/auth-clerk] Auth provider updates must return object users."
508
- ),
509
- changed: true
510
- };
511
- }
512
- throw new Error(
513
- "[@holo-js/auth-clerk] Auth provider adapters must implement update() to persist profile changes."
514
- );
515
- }
516
- async function ensureNoUnexpectedEmailCollision(adapter, providerName, profile, currentUserId) {
517
- const resolvedEmail = resolvePrimaryEmail(profile).email;
518
- if (!resolvedEmail) {
519
- return;
520
- }
521
- const matched = await findUserByEmail(adapter, resolvedEmail);
522
- if (!matched) {
523
- return;
524
- }
525
- if (requireUserId(
526
- adapter,
527
- matched,
528
- "[@holo-js/auth-clerk] Matched local users must expose a serializable id."
529
- ) !== currentUserId) {
530
- throw new ClerkAuthConflictError({
531
- provider: providerName,
532
- clerkUserId: profile.id,
533
- email: resolvedEmail,
534
- message: `[@holo-js/auth-clerk] Clerk email "${resolvedEmail}" collides with a different local user.`
535
- });
536
- }
537
- }
538
474
  function isEmailVerificationRequired() {
539
475
  return authRuntimeInternals3.getRuntimeBindings().config.emailVerification.required === true;
540
476
  }
@@ -554,47 +490,6 @@ function createIdentityRecord(input) {
554
490
  updatedAt: now
555
491
  });
556
492
  }
557
- async function withIdentitySyncLock(key, callback) {
558
- const previous = clerkIdentitySyncLocks.get(key) ?? Promise.resolve();
559
- let release;
560
- const gate = new Promise((resolve) => {
561
- release = resolve;
562
- });
563
- const current = previous.then(() => gate);
564
- clerkIdentitySyncLocks.set(key, current);
565
- await previous;
566
- try {
567
- return await callback();
568
- } finally {
569
- release();
570
- if (clerkIdentitySyncLocks.get(key) === current) {
571
- clerkIdentitySyncLocks.delete(key);
572
- }
573
- }
574
- }
575
- async function claimNewIdentity(identityStore, identity) {
576
- if (identityStore.claim) {
577
- return await identityStore.claim(identity);
578
- }
579
- await identityStore.save(identity);
580
- return identity;
581
- }
582
- function sameUserId(left, right) {
583
- return String(left) === String(right);
584
- }
585
- async function resolveClaimedIdentityUser(adapter, identity, fallback) {
586
- if (sameUserId(identity.userId, fallback.userId)) {
587
- return fallback.user;
588
- }
589
- const claimedUser = requireUserRecord(
590
- await adapter.findById(identity.userId),
591
- "[@holo-js/auth-clerk] Claimed Clerk identities must reference an existing local user."
592
- );
593
- if (adapter.delete) {
594
- await adapter.delete(fallback.userId);
595
- }
596
- return claimedUser;
597
- }
598
493
  function getSessionTokenFromRequest(request, sessionCookie) {
599
494
  const authorization = request.headers.get("authorization")?.trim();
600
495
  if (authorization) {
@@ -803,124 +698,41 @@ async function syncIdentity(session, provider, options = {}) {
803
698
  if (verificationRequired && !verifiedEmail) {
804
699
  throw new Error(`[@holo-js/auth-clerk] Clerk identity "${profile.id}" must provide a verified email address.`);
805
700
  }
806
- return await withIdentitySyncLock(`${providerName}:${profile.id}`, async () => {
807
- const identityStore = getBindings().identityStore;
808
- const existingIdentity = await identityStore.findByProviderUserId(providerName, profile.id);
809
- if (existingIdentity) {
810
- const existingLinkedUser = await adapter.findById(existingIdentity.userId);
811
- let linkedUser = existingLinkedUser ? requireUserRecord(existingLinkedUser, "[@holo-js/auth-clerk] Auth provider lookups must return object users.") : null;
812
- if (!linkedUser) {
813
- const emailMatchedUser = verifiedEmail ? await findUserByEmail(adapter, verifiedEmail) : null;
814
- if (emailMatchedUser) {
815
- throw new ClerkAuthConflictError({
816
- provider: providerName,
817
- clerkUserId: profile.id,
818
- email: verifiedEmail,
819
- message: `[@holo-js/auth-clerk] Clerk email "${verifiedEmail}" matches an existing local user and must be linked explicitly.`
820
- });
821
- }
822
- linkedUser = requireUserRecord(
823
- await adapter.create(resolveCreateUserInput(profile, options.user)),
824
- "[@holo-js/auth-clerk] Auth provider create() must return an object user."
825
- );
826
- const relinked = await updateLocalUser(adapter, linkedUser, resolveUpdateUserInput(profile, options.user));
827
- const relinkedUser = relinked.user;
828
- const identity3 = createIdentityRecord({
829
- provider: providerName,
830
- guard,
831
- authProvider,
832
- userId: requireUserId(
833
- adapter,
834
- relinkedUser,
835
- "[@holo-js/auth-clerk] Relinked local users must expose a serializable id."
836
- ),
837
- profile,
838
- previous: existingIdentity
839
- });
840
- await identityStore.save(identity3);
841
- return Object.freeze({
842
- provider: providerName,
843
- guard,
844
- authProvider,
845
- status: "relinked",
846
- user: serializeLocalUser(adapter, relinkedUser, authProvider),
847
- identity: identity3,
848
- session
849
- });
850
- }
851
- await ensureNoUnexpectedEmailCollision(
852
- adapter,
853
- providerName,
854
- profile,
855
- requireUserId(
856
- adapter,
857
- linkedUser,
858
- "[@holo-js/auth-clerk] Linked local users must expose a serializable id."
859
- )
860
- );
861
- const updated = await updateLocalUser(adapter, linkedUser, resolveUpdateUserInput(profile, options.user));
862
- const identity2 = createIdentityRecord({
863
- provider: providerName,
864
- guard,
865
- authProvider,
866
- userId: requireUserId(
867
- adapter,
868
- updated.user,
869
- "[@holo-js/auth-clerk] Updated local users must expose a serializable id."
870
- ),
871
- profile,
872
- previous: existingIdentity
873
- });
874
- await identityStore.save(identity2);
875
- return Object.freeze({
876
- provider: providerName,
877
- guard,
878
- authProvider,
879
- status: updated.changed ? "updated" : "linked",
880
- user: serializeLocalUser(adapter, updated.user, authProvider),
881
- identity: identity2,
882
- session
883
- });
884
- }
885
- let localUser = verifiedEmail ? await findUserByEmail(adapter, verifiedEmail) : null;
886
- if (localUser) {
887
- throw new ClerkAuthConflictError({
888
- provider: providerName,
889
- clerkUserId: profile.id,
890
- email: verifiedEmail,
891
- message: `[@holo-js/auth-clerk] Clerk email "${verifiedEmail}" matches an existing local user and must be linked explicitly.`
892
- });
893
- }
894
- localUser = requireUserRecord(
895
- await adapter.create(resolveCreateUserInput(profile, options.user)),
896
- "[@holo-js/auth-clerk] Auth provider create() must return an object user."
897
- );
898
- const identity = createIdentityRecord({
701
+ const synchronized = await authRuntimeInternals3.syncHostedIdentity({
702
+ provider: providerName,
703
+ providerUserId: profile.id,
704
+ adapter,
705
+ identityStore: getBindings().identityStore,
706
+ ownership: "serialized-claim",
707
+ verifiedEmail,
708
+ email: resolvedEmail.email,
709
+ errorPrefix: "[@holo-js/auth-clerk]",
710
+ identityLabel: "Clerk",
711
+ createUserInput: () => resolveCreateUserInput(profile, options.user),
712
+ updateUserInput: () => resolveUpdateUserInput(profile, options.user),
713
+ createIdentity: (userId, previous) => createIdentityRecord({
899
714
  provider: providerName,
900
715
  guard,
901
716
  authProvider,
902
- userId: requireUserId(
903
- adapter,
904
- localUser,
905
- "[@holo-js/auth-clerk] Created local users must expose a serializable id."
906
- ),
907
- profile
908
- });
909
- const claimedIdentity = await claimNewIdentity(identityStore, identity);
910
- const claimedUser = await resolveClaimedIdentityUser(adapter, claimedIdentity, {
911
- user: localUser,
912
- userId: identity.userId
913
- });
914
- const claimedStatus = sameUserId(claimedIdentity.userId, identity.userId) ? "created" : "linked";
915
- return Object.freeze({
717
+ userId,
718
+ profile,
719
+ previous
720
+ }),
721
+ conflict: (email, collision) => new ClerkAuthConflictError({
916
722
  provider: providerName,
917
- guard,
918
- authProvider,
919
- status: claimedStatus,
920
- user: serializeLocalUser(adapter, claimedUser, authProvider),
921
- identity: claimedIdentity,
922
- session
923
- });
723
+ clerkUserId: profile.id,
724
+ email,
725
+ message: collision ? `[@holo-js/auth-clerk] Clerk email "${email}" collides with a different local user.` : `[@holo-js/auth-clerk] Clerk email "${email}" matches an existing local user and must be linked explicitly.`
726
+ })
727
+ });
728
+ return Object.freeze({
729
+ provider: providerName,
730
+ guard,
731
+ authProvider,
732
+ status: synchronized.status,
733
+ user: serializeLocalUser(adapter, synchronized.user, authProvider),
734
+ identity: synchronized.identity,
735
+ session
924
736
  });
925
737
  }
926
738
  async function authenticate(input, provider) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holo-js/auth-clerk",
3
- "version": "0.3.16",
3
+ "version": "0.3.17",
4
4
  "description": "Holo-JS Framework - Clerk auth integration contracts",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -24,8 +24,8 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "@clerk/backend": "^3.4.7",
27
- "@holo-js/auth": "^0.3.16",
28
- "@holo-js/session": "^0.3.16"
27
+ "@holo-js/auth": "^0.3.17",
28
+ "@holo-js/session": "^0.3.17"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "^22.10.2",