@igstack/app-catalog-backend-core 6.1.6-alpha-20260927042212 → 6.1.6

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.
@@ -2,6 +2,7 @@ import { AcTrpcContext } from "../../server/acTrpcContext.mjs";
2
2
  import { BetterAuth } from "./auth.mjs";
3
3
  import * as _trpc_server0 from "@trpc/server";
4
4
  import { TRPCRootObject } from "@trpc/server";
5
+ import * as better_auth0 from "better-auth";
5
6
 
6
7
  //#region src/modules/auth/authRouter.d.ts
7
8
  /**
@@ -38,7 +39,7 @@ declare function createAuthRouter(t: TRPCRootObject<AcTrpcContext, {}, {}>, auth
38
39
  getProviders: _trpc_server0.TRPCQueryProcedure<{
39
40
  input: void;
40
41
  output: {
41
- providers: string[];
42
+ providers: better_auth0.LiteralString[];
42
43
  devLoginEnabled: boolean;
43
44
  };
44
45
  meta: {};
@@ -1 +1 @@
1
- {"version":3,"file":"authRouter.d.mts","names":[],"sources":["../../../src/modules/auth/authRouter.ts"],"mappings":";;;;;;;;;AAWA;;;iBAAgB,gBAAA,CACd,CAAA,EAAG,cAAA,CAAe,aAAA,WAClB,IAAA,GAAO,UAAA,EACP,OAAA;EAAY,eAAA;AAAA,kBAA2B,eAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA0D7B,UAAA,GAAa,UAAA,QAAkB,gBAAA"}
1
+ {"version":3,"file":"authRouter.d.mts","names":[],"sources":["../../../src/modules/auth/authRouter.ts"],"mappings":";;;;;;;;;;;AAUA;;iBAAgB,gBAAA,CACd,CAAA,EAAG,cAAA,CAAe,aAAA,WAClB,IAAA,GAAO,UAAA,EACP,OAAA;EAAY,eAAA;AAAA,kBAA2B,eAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA+B7B,UAAA,GAAa,UAAA,QAAkB,gBAAA"}
@@ -16,23 +16,9 @@ function createAuthRouter(t, auth, options) {
16
16
  isAdmin: ctx.isAdmin
17
17
  };
18
18
  }),
19
- getProviders: publicProcedure.query(() => {
20
- const providers = [];
21
- const authOptions = auth?.options;
22
- if (authOptions?.socialProviders) {
23
- const socialProviders = authOptions.socialProviders;
24
- Object.keys(socialProviders).forEach((key) => {
25
- if (socialProviders[key]) providers.push(key);
26
- });
27
- }
28
- if (authOptions?.plugins) authOptions.plugins.forEach((plugin) => {
29
- if (plugin.id !== "generic-oauth" || !plugin.options?.config) return;
30
- (Array.isArray(plugin.options.config) ? plugin.options.config : [plugin.options.config]).forEach((config) => {
31
- if (config.providerId) providers.push(config.providerId);
32
- });
33
- });
19
+ getProviders: publicProcedure.query(async () => {
34
20
  return {
35
- providers,
21
+ providers: (await auth?.$context)?.socialProviders.map((p) => p.id) ?? [],
36
22
  devLoginEnabled: options?.devLoginEnabled ?? false
37
23
  };
38
24
  })
@@ -1 +1 @@
1
- {"version":3,"file":"authRouter.mjs","names":[],"sources":["../../../src/modules/auth/authRouter.ts"],"sourcesContent":["import type { BetterAuthPlugin } from 'better-auth'\nimport type { TRPCRootObject } from '@trpc/server'\nimport type { AcTrpcContext } from '../../server/acTrpcContext'\nimport type { BetterAuth } from './auth'\n\n/**\n * Create auth tRPC procedures\n * @param t - tRPC instance\n * @param auth - Better Auth instance (optional, for future extensions)\n * @returns tRPC router with auth procedures\n */\nexport function createAuthRouter(\n t: TRPCRootObject<AcTrpcContext, {}, {}>,\n auth?: BetterAuth,\n options?: { devLoginEnabled?: boolean },\n) {\n const router = t.router\n const publicProcedure = t.procedure\n\n return router({\n getSession: publicProcedure.query(async ({ ctx }) => {\n return {\n user: ctx.user ?? null,\n isAuthenticated: !!ctx.user,\n isAdmin: ctx.isAdmin,\n }\n }),\n getProviders: publicProcedure.query(() => {\n // Return configured social providers and OAuth providers from plugins\n const providers: string[] = []\n const authOptions = auth?.options\n\n // Add built-in social providers (github, google, etc.)\n if (authOptions?.socialProviders) {\n const socialProviders = authOptions.socialProviders as Record<\n string,\n unknown\n >\n Object.keys(socialProviders).forEach((key) => {\n if (socialProviders[key]) {\n providers.push(key)\n }\n })\n }\n\n // Add OAuth providers from plugins (like Okta via genericOAuth). They are\n // absent from socialProviders above: the plugin only merges them into the\n // auth context at init time, not into the static options.\n if (authOptions?.plugins) {\n const plugins = authOptions.plugins\n plugins.forEach((plugin: BetterAuthPlugin) => {\n if (plugin.id !== 'generic-oauth' || !plugin.options?.config) {\n return\n }\n const configs: { providerId?: string }[] = Array.isArray(\n plugin.options.config,\n )\n ? plugin.options.config\n : [plugin.options.config]\n configs.forEach((config) => {\n if (config.providerId) {\n providers.push(config.providerId)\n }\n })\n })\n }\n\n return { providers, devLoginEnabled: options?.devLoginEnabled ?? false }\n }),\n })\n}\n\nexport type AuthRouter = ReturnType<typeof createAuthRouter>\n"],"mappings":";;;;;;;AAWA,SAAgB,iBACd,GACA,MACA,SACA;CACA,MAAM,SAAS,EAAE;CACjB,MAAM,kBAAkB,EAAE;AAE1B,QAAO,OAAO;EACZ,YAAY,gBAAgB,MAAM,OAAO,EAAE,UAAU;AACnD,UAAO;IACL,MAAM,IAAI,QAAQ;IAClB,iBAAiB,CAAC,CAAC,IAAI;IACvB,SAAS,IAAI;IACd;IACD;EACF,cAAc,gBAAgB,YAAY;GAExC,MAAM,YAAsB,EAAE;GAC9B,MAAM,cAAc,MAAM;AAG1B,OAAI,aAAa,iBAAiB;IAChC,MAAM,kBAAkB,YAAY;AAIpC,WAAO,KAAK,gBAAgB,CAAC,SAAS,QAAQ;AAC5C,SAAI,gBAAgB,KAClB,WAAU,KAAK,IAAI;MAErB;;AAMJ,OAAI,aAAa,QAEf,CADgB,YAAY,QACpB,SAAS,WAA6B;AAC5C,QAAI,OAAO,OAAO,mBAAmB,CAAC,OAAO,SAAS,OACpD;AAOF,KAL2C,MAAM,QAC/C,OAAO,QAAQ,OAChB,GACG,OAAO,QAAQ,SACf,CAAC,OAAO,QAAQ,OAAO,EACnB,SAAS,WAAW;AAC1B,SAAI,OAAO,WACT,WAAU,KAAK,OAAO,WAAW;MAEnC;KACF;AAGJ,UAAO;IAAE;IAAW,iBAAiB,SAAS,mBAAmB;IAAO;IACxE;EACH,CAAC"}
1
+ {"version":3,"file":"authRouter.mjs","names":[],"sources":["../../../src/modules/auth/authRouter.ts"],"sourcesContent":["import type { TRPCRootObject } from '@trpc/server'\nimport type { AcTrpcContext } from '../../server/acTrpcContext'\nimport type { BetterAuth } from './auth'\n\n/**\n * Create auth tRPC procedures\n * @param t - tRPC instance\n * @param auth - Better Auth instance (optional, for future extensions)\n * @returns tRPC router with auth procedures\n */\nexport function createAuthRouter(\n t: TRPCRootObject<AcTrpcContext, {}, {}>,\n auth?: BetterAuth,\n options?: { devLoginEnabled?: boolean },\n) {\n const router = t.router\n const publicProcedure = t.procedure\n\n return router({\n getSession: publicProcedure.query(async ({ ctx }) => {\n return {\n user: ctx.user ?? null,\n isAuthenticated: !!ctx.user,\n isAdmin: ctx.isAdmin,\n }\n }),\n getProviders: publicProcedure.query(async () => {\n // Read the providers better-auth actually resolved, not the static\n // config. The generic OAuth plugin resolves OIDC discovery once, in its\n // init, and silently skips a provider whose discovery document failed to\n // load -- for the whole life of that process. Deriving the list from the\n // static config would keep offering a sign-in button that answers 404\n // \"Provider not found\". The resolved context holds both halves: built-in\n // social providers and the generic ones that survived discovery.\n const authContext = await auth?.$context\n\n return {\n providers: authContext?.socialProviders.map((p) => p.id) ?? [],\n devLoginEnabled: options?.devLoginEnabled ?? false,\n }\n }),\n })\n}\n\nexport type AuthRouter = ReturnType<typeof createAuthRouter>\n"],"mappings":";;;;;;;AAUA,SAAgB,iBACd,GACA,MACA,SACA;CACA,MAAM,SAAS,EAAE;CACjB,MAAM,kBAAkB,EAAE;AAE1B,QAAO,OAAO;EACZ,YAAY,gBAAgB,MAAM,OAAO,EAAE,UAAU;AACnD,UAAO;IACL,MAAM,IAAI,QAAQ;IAClB,iBAAiB,CAAC,CAAC,IAAI;IACvB,SAAS,IAAI;IACd;IACD;EACF,cAAc,gBAAgB,MAAM,YAAY;AAU9C,UAAO;IACL,YAHkB,MAAM,MAAM,WAGN,gBAAgB,KAAK,MAAM,EAAE,GAAG,IAAI,EAAE;IAC9D,iBAAiB,SAAS,mBAAmB;IAC9C;IACD;EACH,CAAC"}
@@ -3,6 +3,7 @@ import { AcTrpcContext } from "./acTrpcContext.mjs";
3
3
  import { BetterAuth } from "../modules/auth/auth.mjs";
4
4
  import { CommentView } from "../modules/comments/service.mjs";
5
5
  import * as _trpc_server0 from "@trpc/server";
6
+ import * as better_auth0 from "better-auth";
6
7
 
7
8
  //#region src/server/controller.d.ts
8
9
  /**
@@ -118,7 +119,7 @@ declare function createTrpcRouter(auth?: BetterAuth, options?: {
118
119
  getProviders: _trpc_server0.TRPCQueryProcedure<{
119
120
  input: void;
120
121
  output: {
121
- providers: string[];
122
+ providers: better_auth0.LiteralString[];
122
123
  devLoginEnabled: boolean;
123
124
  };
124
125
  meta: {};
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.mts","names":[],"sources":["../../src/server/controller.ts"],"mappings":";;;;;;;;;;;iBAmCgB,gBAAA,CACd,IAAA,GAAO,UAAA,EACP,OAAA;EAAY,eAAA;AAAA,kBAA2B,eAAA;OADtB,aAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAwCP,UAAA,GAAa,UAAA,QAAkB,gBAAA"}
1
+ {"version":3,"file":"controller.d.mts","names":[],"sources":["../../src/server/controller.ts"],"mappings":";;;;;;;;;;;;iBAmCgB,gBAAA,CACd,IAAA,GAAO,UAAA,EACP,OAAA;EAAY,eAAA;AAAA,kBAA2B,eAAA;OADtB,aAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAwCP,UAAA,GAAa,UAAA,QAAkB,gBAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@igstack/app-catalog-backend-core",
3
- "version": "6.1.6-alpha-20260927042212",
3
+ "version": "6.1.6",
4
4
  "description": "Backend core library for App Catalog",
5
5
  "homepage": "https://github.com/lislon/app-catalog",
6
6
  "repository": {
@@ -44,8 +44,8 @@
44
44
  "tsyringe": "^4.10.0",
45
45
  "yaml": "^2.8.0",
46
46
  "zod": "^4.3.5",
47
- "@igstack/app-catalog-shared-core": "6.1.6-alpha-20260927042212",
48
- "@igstack/app-catalog-table-sync": "6.1.6-alpha-20260927042212"
47
+ "@igstack/app-catalog-shared-core": "6.1.6",
48
+ "@igstack/app-catalog-table-sync": "6.1.6"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@tanstack/vite-config": "^0.4.3",
@@ -72,7 +72,7 @@
72
72
  "engines": {
73
73
  "node": ">=24"
74
74
  },
75
- "gitHead": "fd9dfe8da430f2adacd331609440085e52de0286",
75
+ "gitHead": "0a2ab15f93f8e6b362fa6bb09e3e06e7fed68abd",
76
76
  "scripts": {
77
77
  "build": "tsdown",
78
78
  "build:lenient": "tsdown",
@@ -1,3 +1,5 @@
1
+ import { betterAuth } from 'better-auth'
2
+ import { memoryAdapter } from 'better-auth/adapters/memory'
1
3
  import { genericOAuth } from 'better-auth/plugins'
2
4
  import { describe, expect, it } from 'vitest'
3
5
  import { createAuthRouter } from '../modules/auth/authRouter'
@@ -7,60 +9,85 @@ import { t } from '../server/trpcSetup'
7
9
 
8
10
  // The sign-in page renders one button per name getProviders returns, so an
9
11
  // upstream rename of the plugin id or of `options.config` silently leaves users
10
- // with no way to sign in. The real plugin is instantiated here on purpose --
11
- // a hand-written stub of its shape would keep passing through that rename.
12
- function providers(auth: Partial<BetterAuth>) {
13
- const caller = createAuthRouter(t, auth as BetterAuth).createCaller(
14
- {} as AcTrpcContext,
15
- )
16
- return caller.getProviders()
12
+ // with no way to sign in. A real betterAuth instance is built here on purpose --
13
+ // a hand-written stub of the auth context would keep passing through that
14
+ // rename, and would not run the plugin's own init, which is where the provider
15
+ // list is actually decided.
16
+ function auth(options: Record<string, unknown>): BetterAuth {
17
+ return betterAuth({
18
+ database: memoryAdapter({}),
19
+ secret: 'test-secret-that-is-long-enough',
20
+ baseURL: 'http://localhost',
21
+ // Provider skips are logged as errors; the skip is the expectation here.
22
+ logger: { disabled: true },
23
+ ...options,
24
+ }) as BetterAuth
25
+ }
26
+
27
+ function providers(instance: BetterAuth) {
28
+ return createAuthRouter(t, instance)
29
+ .createCaller({} as AcTrpcContext)
30
+ .getProviders()
31
+ }
32
+
33
+ // Needs no discovery document, so it resolves without touching the network.
34
+ const resolvable = {
35
+ providerId: 'okta',
36
+ clientId: 'id',
37
+ clientSecret: 'secret',
38
+ authorizationUrl: 'https://example.com/oauth2/v1/authorize',
39
+ tokenUrl: 'https://example.com/oauth2/v1/token',
40
+ }
41
+
42
+ // Port 1 on the loopback refuses instantly, with no DNS lookup and no reliance
43
+ // on the CI network -- which is exactly the unreachable-discovery-endpoint
44
+ // condition this guards. better-auth passes no timeout to its fetch, so a host
45
+ // that black-holed the connection instead would hang to vitest's default.
46
+ const unresolvable = {
47
+ providerId: 'okta',
48
+ clientId: 'id',
49
+ clientSecret: 'secret',
50
+ discoveryUrl: 'http://127.0.0.1:1/.well-known/openid-configuration',
17
51
  }
18
52
 
19
53
  describe('auth getProviders', () => {
20
54
  it('lists a provider registered through the generic OAuth plugin', async () => {
21
- const { providers: names } = await providers({
22
- options: {
23
- plugins: [
24
- genericOAuth({
25
- config: [
26
- {
27
- providerId: 'okta',
28
- clientId: 'id',
29
- clientSecret: 'secret',
30
- discoveryUrl:
31
- 'https://example.invalid/.well-known/openid-configuration',
32
- },
33
- ],
34
- }),
35
- ],
36
- },
37
- })
55
+ const { providers: names } = await providers(
56
+ auth({ plugins: [genericOAuth({ config: [resolvable] })] }),
57
+ )
38
58
 
39
59
  expect(names).toEqual(['okta'])
40
60
  })
41
61
 
42
62
  it('lists built-in social providers alongside plugin ones', async () => {
43
- const { providers: names } = await providers({
44
- options: {
45
- socialProviders: {
46
- github: { clientId: 'id', clientSecret: 'secret' },
47
- },
63
+ const { providers: names } = await providers(
64
+ auth({
65
+ socialProviders: { github: { clientId: 'id', clientSecret: 'secret' } },
66
+ plugins: [genericOAuth({ config: [resolvable] })],
67
+ }),
68
+ )
69
+
70
+ // The plugin prepends its providers, so the order is upstream's to choose --
71
+ // but the set is not, and toContain alone would pass on a duplicate or an
72
+ // extra name.
73
+ expect([...names].sort()).toEqual(['github', 'okta'])
74
+ })
75
+
76
+ // better-auth 1.7 resolves OIDC discovery once, in the plugin's init, and
77
+ // skips a provider whose discovery failed. Deriving the list from the static
78
+ // config instead would still offer the button, and clicking it 400s with
79
+ // "Provider not found" for the whole life of that pod.
80
+ it('omits a provider whose discovery failed, and keeps the ones that resolved', async () => {
81
+ const { providers: names } = await providers(
82
+ auth({
48
83
  plugins: [
49
84
  genericOAuth({
50
- config: [
51
- {
52
- providerId: 'okta',
53
- clientId: 'id',
54
- clientSecret: 'secret',
55
- discoveryUrl:
56
- 'https://example.invalid/.well-known/openid-configuration',
57
- },
58
- ],
85
+ config: [unresolvable, { ...resolvable, providerId: 'other-idp' }],
59
86
  }),
60
87
  ],
61
- },
62
- })
88
+ }),
89
+ )
63
90
 
64
- expect(names).toEqual(['github', 'okta'])
91
+ expect(names).toEqual(['other-idp'])
65
92
  })
66
93
  })
@@ -1,4 +1,3 @@
1
- import type { BetterAuthPlugin } from 'better-auth'
2
1
  import type { TRPCRootObject } from '@trpc/server'
3
2
  import type { AcTrpcContext } from '../../server/acTrpcContext'
4
3
  import type { BetterAuth } from './auth'
@@ -25,47 +24,20 @@ export function createAuthRouter(
25
24
  isAdmin: ctx.isAdmin,
26
25
  }
27
26
  }),
28
- getProviders: publicProcedure.query(() => {
29
- // Return configured social providers and OAuth providers from plugins
30
- const providers: string[] = []
31
- const authOptions = auth?.options
27
+ getProviders: publicProcedure.query(async () => {
28
+ // Read the providers better-auth actually resolved, not the static
29
+ // config. The generic OAuth plugin resolves OIDC discovery once, in its
30
+ // init, and silently skips a provider whose discovery document failed to
31
+ // load -- for the whole life of that process. Deriving the list from the
32
+ // static config would keep offering a sign-in button that answers 404
33
+ // "Provider not found". The resolved context holds both halves: built-in
34
+ // social providers and the generic ones that survived discovery.
35
+ const authContext = await auth?.$context
32
36
 
33
- // Add built-in social providers (github, google, etc.)
34
- if (authOptions?.socialProviders) {
35
- const socialProviders = authOptions.socialProviders as Record<
36
- string,
37
- unknown
38
- >
39
- Object.keys(socialProviders).forEach((key) => {
40
- if (socialProviders[key]) {
41
- providers.push(key)
42
- }
43
- })
44
- }
45
-
46
- // Add OAuth providers from plugins (like Okta via genericOAuth). They are
47
- // absent from socialProviders above: the plugin only merges them into the
48
- // auth context at init time, not into the static options.
49
- if (authOptions?.plugins) {
50
- const plugins = authOptions.plugins
51
- plugins.forEach((plugin: BetterAuthPlugin) => {
52
- if (plugin.id !== 'generic-oauth' || !plugin.options?.config) {
53
- return
54
- }
55
- const configs: { providerId?: string }[] = Array.isArray(
56
- plugin.options.config,
57
- )
58
- ? plugin.options.config
59
- : [plugin.options.config]
60
- configs.forEach((config) => {
61
- if (config.providerId) {
62
- providers.push(config.providerId)
63
- }
64
- })
65
- })
37
+ return {
38
+ providers: authContext?.socialProviders.map((p) => p.id) ?? [],
39
+ devLoginEnabled: options?.devLoginEnabled ?? false,
66
40
  }
67
-
68
- return { providers, devLoginEnabled: options?.devLoginEnabled ?? false }
69
41
  }),
70
42
  })
71
43
  }