{"version":3,"file":"native-import.mjs","names":[],"sources":["../../../src/internal/oauth/native-import.ts"],"sourcesContent":["import { snapshotOAuth } from \"@yielded/auth/OAuth\";\nimport { SecurityRevision } from \"@yielded/auth/Sessions\";\nimport * as Digest from \"effect/crypto/Digest\";\nimport * as SecureRandom from \"effect/crypto/SecureRandom\";\nimport * as Effect from \"effect/Effect\";\n\nimport type {\n  OAuthAuthorityTable,\n  OAuthCredentialTable,\n  OAuthOwnershipTable,\n} from \"../models/oauth-model\";\nimport type { NativeSqlTables } from \"../native-sql-table\";\nimport { makeConditionalSqlInsert } from \"../session-native-record\";\nimport { exactSqlText } from \"../sql-change\";\nimport { makeSqlCommitExecutor, SqlBatchCommit } from \"../sql-commit\";\nimport type { TableModel } from \"../table-model\";\nimport { IdentityImport, ImportedIdentity, IdentityImports } from \"./identity-import\";\nimport { makeOAuthNativeMutation } from \"./native-mutation\";\nimport { makeOAuthNativeState, type OAuthNativeReadMapping } from \"./native-state\";\nimport { nonce, oauthIdentityKey, unavailable } from \"./state\";\n\ntype ImportMapping = Omit<OAuthNativeReadMapping, \"ownership\" | \"credential\" | \"authority\"> & {\n  readonly ownership: OAuthOwnershipTable<TableModel, unknown>;\n  readonly credential: OAuthCredentialTable<TableModel, unknown>;\n  readonly authority: OAuthAuthorityTable<TableModel, unknown>;\n};\n\nexport const makeNativeOAuthIdentityImports = Effect.fnUntraced(function* (\n  tables: NativeSqlTables,\n  mapping: ImportMapping,\n) {\n  const batch = yield* SqlBatchCommit;\n  const executor = yield* makeSqlCommitExecutor(unavailable);\n  const conditionalInsert = yield* makeConditionalSqlInsert();\n  const crypto = yield* SecureRandom.SecureRandom;\n  const digests = yield* Digest.Digest;\n  const state = yield* makeOAuthNativeState(tables, mapping);\n  const mutation = yield* makeOAuthNativeMutation(tables, mapping, batch !== undefined);\n  const { sql, subject, authority } = mutation;\n  const s = mapping.subject;\n  const c = mapping.credential;\n  const a = mapping.authority;\n  const credential = tables(c.table);\n\n  const exact = (key: string, value: unknown) =>\n    exactSqlText(sql, credential.column(key), credential.value(key, value));\n\n  const importIdentity: IdentityImports[\"Service\"][\"importIdentity\"] = (original) => {\n    const work = Effect.gen(function* () {\n      const input = yield* snapshotOAuth(IdentityImport, original);\n      const current = yield* mutation.captureSubject(input.subjectId, true);\n\n      if (current === undefined) return yield* unavailable();\n      const key = yield* oauthIdentityKey(input);\n\n      const found =\n        yield* sql`select 1 from ${credential.name} where ${exact(c.identityKey, key)} limit 2`;\n\n      if (found.length > 1) return yield* unavailable();\n      if (found.length !== 0) {\n        const existing = yield* state.resolve(input.moduleId, input);\n\n        if (existing === undefined || existing.revision.subjectId !== input.subjectId)\n          return yield* unavailable();\n        // D1 repeats the observed credential and subject revision in its physical batch.\n        yield* mutation.assert(\n          sql.and([\n            mutation.authorityCondition(current.nativeId, existing.revision),\n            sql`exists(select 1 from ${credential.name} where ${exact(c.identityKey, key)} and ${exact(c.credentialId, existing.credentialId)} and ${exact(c.credentialRevision, existing.credentialRevision)} and ${tables.expression(c.activeCondition)})`,\n            sql`exists(select 1 from ${mutation.ownership.name} where ${mutation.ownerCondition(key, current.nativeId)})`,\n          ]),\n        );\n\n        return yield* snapshotOAuth(ImportedIdentity, existing);\n      }\n\n      // A subject may have no methods yet. Locking it still serializes import with\n      // account-management mutations; every new factor advances securityRevision.\n      const subjectCondition = mutation.subjectCondition(current.nativeId, current);\n      const before = sql`exists(select 1 from ${subject.name} where ${subjectCondition})`;\n\n      const factors =\n        yield* sql`select 1 from ${authority.name} where ${authority.column(a.subjectId)} = ${authority.value(a.subjectId, current.nativeId)} and ${tables.expression(a.activeCondition)} limit 64`;\n\n      if (factors.length >= 64) return yield* unavailable();\n      const identityKey = yield* mutation.ensureOwnership(input, current.nativeId);\n\n      if (identityKey === undefined) return yield* unavailable();\n      const credentialId = yield* nonce;\n      const credentialRevision = SecurityRevision.make(yield* nonce);\n      const securityRevision = SecurityRevision.make(yield* nonce);\n\n      const login = {\n        ...c.encodeInsert({\n          moduleId: input.moduleId,\n          subjectId: current.nativeId,\n          identityKey,\n          credentialId,\n          credentialRevision,\n        }),\n        [c.moduleId]: input.moduleId,\n        [c.subjectId]: current.nativeId,\n        [c.identityKey]: identityKey,\n        [c.credentialId]: credentialId,\n        [c.credentialRevision]: credentialRevision,\n      };\n\n      const factor = {\n        ...a.encodeInsert({\n          subjectId: current.nativeId,\n          credentialId,\n          revision: credentialRevision,\n        }),\n        [a.subjectId]: current.nativeId,\n        [a.credentialId]: credentialId,\n        [a.revision]: credentialRevision,\n      };\n\n      if (!c.isActiveStatus(login[c.status]) || !a.isActiveStatus(factor[a.status]))\n        return yield* unavailable();\n      if ((yield* mutation.change(yield* conditionalInsert(credential, login, before))) !== 1)\n        return yield* unavailable();\n      if ((yield* mutation.change(yield* conditionalInsert(authority, factor, before))) !== 1)\n        return yield* unavailable();\n      if (\n        (yield* mutation.change(\n          sql`${subject.update({ [s.securityRevision]: securityRevision })} where ${subjectCondition}`,\n        )) !== 1\n      )\n        return yield* unavailable();\n\n      return yield* snapshotOAuth(ImportedIdentity, { credentialId, credentialRevision });\n    }).pipe(\n      Effect.provideService(SecureRandom.SecureRandom, crypto),\n      Effect.provideService(Digest.Digest, digests),\n    );\n\n    return batch === undefined\n      ? executor.run(work)\n      : executor.batch(work).pipe(Effect.provideService(SqlBatchCommit, batch));\n  };\n\n  return IdentityImports.of({ importIdentity });\n});\n"],"mappings":"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"}