@securitydept/basic-auth-context-client 0.3.0-beta.2 → 0.3.0-beta.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"client-D_0eAARH.mjs","names":["BasicAuthBoundaryKindValues"],"sources":["../src/schemas.ts","../src/client.ts"],"sourcesContent":["// basic-auth-context-client — config validation schema\n//\n// Uses @standard-schema via the foundation createSchema helper to validate\n// BasicAuthContextClientConfig at the cross-boundary entry point.\n\nimport { createSchema } from \"@securitydept/client\";\nimport type { BasicAuthContextClientConfig } from \"./types\";\n\nfunction isNonEmptyString(v: unknown): v is string {\n\treturn typeof v === \"string\" && v.length > 0;\n}\n\nfunction isObject(v: unknown): v is Record<string, unknown> {\n\treturn typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\nfunction validateZoneConfig(input: unknown, index: number) {\n\tconst issues: Array<{ message: string }> = [];\n\tif (!isObject(input)) {\n\t\tissues.push({ message: `zones[${index}]: expected an object` });\n\t\treturn { issues };\n\t}\n\tif (!isNonEmptyString(input.zonePrefix)) {\n\t\tissues.push({\n\t\t\tmessage: `zones[${index}].zonePrefix: expected a non-empty string`,\n\t\t});\n\t}\n\tif (\n\t\tinput.loginSubpath !== undefined &&\n\t\ttypeof input.loginSubpath !== \"string\"\n\t) {\n\t\tissues.push({\n\t\t\tmessage: `zones[${index}].loginSubpath: expected a string if provided`,\n\t\t});\n\t}\n\tif (\n\t\tinput.logoutSubpath !== undefined &&\n\t\ttypeof input.logoutSubpath !== \"string\"\n\t) {\n\t\tissues.push({\n\t\t\tmessage: `zones[${index}].logoutSubpath: expected a string if provided`,\n\t\t});\n\t}\n\treturn issues.length > 0 ? { issues } : null;\n}\n\n/**\n * Schema for validating BasicAuthContextClientConfig.\n *\n * Validates:\n * - `baseUrl` is a non-empty string\n * - `zones` is a non-empty array of valid zone configs\n * - each zone has a non-empty `zonePrefix`\n * - optional fields have correct types when provided\n */\nexport const BasicAuthContextClientConfigSchema =\n\tcreateSchema<BasicAuthContextClientConfig>({\n\t\tvalidate(input) {\n\t\t\tconst issues: Array<{ message: string }> = [];\n\n\t\t\tif (!isObject(input)) {\n\t\t\t\treturn { issues: [{ message: \"Expected an object\" }] };\n\t\t\t}\n\n\t\t\tif (!isNonEmptyString(input.baseUrl)) {\n\t\t\t\tissues.push({ message: \"baseUrl: expected a non-empty string\" });\n\t\t\t}\n\n\t\t\tif (!Array.isArray(input.zones) || input.zones.length === 0) {\n\t\t\t\tissues.push({\n\t\t\t\t\tmessage: \"zones: expected a non-empty array of zone configs\",\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tfor (let i = 0; i < input.zones.length; i++) {\n\t\t\t\t\tconst zoneResult = validateZoneConfig(input.zones[i], i);\n\t\t\t\t\tif (zoneResult) {\n\t\t\t\t\t\tissues.push(...zoneResult.issues);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\tinput.postAuthRedirectParam !== undefined &&\n\t\t\t\ttypeof input.postAuthRedirectParam !== \"string\"\n\t\t\t) {\n\t\t\t\tissues.push({\n\t\t\t\t\tmessage: \"postAuthRedirectParam: expected a string if provided\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (issues.length > 0) {\n\t\t\t\treturn { issues };\n\t\t\t}\n\n\t\t\treturn { value: input as unknown as BasicAuthContextClientConfig };\n\t\t},\n\t});\n","import { validateWithSchemaSync } from \"@securitydept/client\";\nimport { BasicAuthContextClientConfigSchema } from \"./schemas\";\nimport type {\n\tAuthGuardResult,\n\tBasicAuthBoundaryKind,\n\tBasicAuthBoundaryObservation,\n\tBasicAuthContextClientConfig,\n\tBasicAuthZoneConfig,\n\tResolvedBasicAuthZone,\n} from \"./types\";\nimport {\n\tAuthGuardRedirectStatus,\n\tAuthGuardResultKind,\n\tBasicAuthBoundaryKind as BasicAuthBoundaryKindValues,\n} from \"./types\";\n\nconst DEFAULT_LOGIN_SUBPATH = \"/login\";\nconst DEFAULT_LOGOUT_SUBPATH = \"/logout\";\nconst DEFAULT_POST_AUTH_REDIRECT_PARAM = \"post_auth_redirect_uri\";\n\nfunction resolveZone(config: BasicAuthZoneConfig): ResolvedBasicAuthZone {\n\tconst prefix = config.zonePrefix.replace(/\\/+$/, \"\");\n\tconst loginSub = config.loginSubpath ?? DEFAULT_LOGIN_SUBPATH;\n\tconst logoutSub = config.logoutSubpath ?? DEFAULT_LOGOUT_SUBPATH;\n\n\treturn {\n\t\tzonePrefix: prefix,\n\t\tloginPath: prefix + loginSub,\n\t\tlogoutPath: prefix + logoutSub,\n\t};\n}\n\nexport function readBasicAuthBoundaryKind(\n\toptions: BasicAuthBoundaryObservation,\n): BasicAuthBoundaryKind {\n\tif (options.status < 400) {\n\t\treturn BasicAuthBoundaryKindValues.Authenticated;\n\t}\n\n\tif (options.challengeHeader) {\n\t\treturn BasicAuthBoundaryKindValues.Challenge;\n\t}\n\n\tif (options.status === 401 && options.requestPath === \"/basic/logout\") {\n\t\treturn BasicAuthBoundaryKindValues.LogoutPoison;\n\t}\n\n\treturn BasicAuthBoundaryKindValues.Unauthorized;\n}\n\n/**\n * Basic Auth Context Client.\n *\n * Thin client that manages zone boundary awareness and redirect instructions.\n * It does NOT manage credentials or HTTP auth headers — those are handled\n * by the browser's native Basic Auth UI triggered by 401 + WWW-Authenticate.\n */\nexport class BasicAuthContextClient {\n\treadonly zones: readonly ResolvedBasicAuthZone[];\n\tprivate readonly _baseUrl: string;\n\tprivate readonly _postAuthRedirectParam: string;\n\n\tconstructor(config: BasicAuthContextClientConfig) {\n\t\t// Deprecation-phase validation: warn on invalid config but do not throw.\n\t\t// A future minor release will escalate this to a hard error.\n\t\t// See 110-TS_SDK_MIGRATIONS.md for the full deprecation note.\n\t\tconst result = validateWithSchemaSync(\n\t\t\tBasicAuthContextClientConfigSchema,\n\t\t\tconfig,\n\t\t);\n\t\tif (!result.success) {\n\t\t\tconsole.warn(\n\t\t\t\t`[BasicAuthContextClient] Deprecated config shape detected (will become an error in a future release): ${result.issues.map((i) => i.message).join(\"; \")}`,\n\t\t\t);\n\t\t}\n\n\t\tthis._baseUrl = (config.baseUrl ?? \"\").replace(/\\/+$/, \"\");\n\t\tthis._postAuthRedirectParam =\n\t\t\tconfig.postAuthRedirectParam ?? DEFAULT_POST_AUTH_REDIRECT_PARAM;\n\t\tthis.zones = (config.zones ?? []).map(resolveZone);\n\t}\n\n\t/** Find the zone that contains the given path. */\n\tzoneForPath(path: string): ResolvedBasicAuthZone | undefined {\n\t\tlet matchedZone: ResolvedBasicAuthZone | undefined;\n\n\t\tfor (const zone of this.zones) {\n\t\t\tconst matches =\n\t\t\t\tpath === zone.zonePrefix || path.startsWith(`${zone.zonePrefix}/`);\n\t\t\tif (!matches) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\t!matchedZone ||\n\t\t\t\tzone.zonePrefix.length > matchedZone.zonePrefix.length\n\t\t\t) {\n\t\t\t\tmatchedZone = zone;\n\t\t\t}\n\t\t}\n\n\t\treturn matchedZone;\n\t}\n\n\tzoneForPrefix(zonePrefix: string): ResolvedBasicAuthZone | undefined {\n\t\treturn this.zones.find((zone) => zone.zonePrefix === zonePrefix);\n\t}\n\n\t/** Check whether a path falls inside any configured zone. */\n\tisInZone(path: string): boolean {\n\t\treturn this.zoneForPath(path) !== undefined;\n\t}\n\n\t/** Build the full login URL for a zone, optionally with a post-auth redirect. */\n\tloginUrl(zone: ResolvedBasicAuthZone, postAuthRedirectUri?: string): string {\n\t\tconst base = this._baseUrl + zone.loginPath;\n\t\tif (postAuthRedirectUri) {\n\t\t\tconst params = new URLSearchParams({\n\t\t\t\t[this._postAuthRedirectParam]: postAuthRedirectUri,\n\t\t\t});\n\t\t\treturn `${base}?${params.toString()}`;\n\t\t}\n\t\treturn base;\n\t}\n\n\tloginUrlForZonePrefix(\n\t\tzonePrefix: string,\n\t\tpostAuthRedirectUri?: string,\n\t): string | null {\n\t\tconst zone = this.zoneForPrefix(zonePrefix);\n\t\treturn zone ? this.loginUrl(zone, postAuthRedirectUri) : null;\n\t}\n\n\t/** Build the full logout URL for a zone. */\n\tlogoutUrl(zone: ResolvedBasicAuthZone): string {\n\t\treturn this._baseUrl + zone.logoutPath;\n\t}\n\n\t/**\n\t * Handle a 401 response: if the current path is inside a zone,\n\t * return a redirect instruction to the zone's login URL.\n\t */\n\thandleUnauthorized(\n\t\tcurrentPath: string,\n\t\tresponseStatus: number,\n\t): AuthGuardResult<null> {\n\t\tif (responseStatus !== 401) {\n\t\t\treturn { kind: AuthGuardResultKind.Ok, value: null };\n\t\t}\n\n\t\tconst zone = this.zoneForPath(currentPath);\n\t\tif (!zone) {\n\t\t\treturn { kind: AuthGuardResultKind.Ok, value: null };\n\t\t}\n\n\t\treturn {\n\t\t\tkind: AuthGuardResultKind.Redirect,\n\t\t\tstatus: AuthGuardRedirectStatus.Found,\n\t\t\tlocation: this.loginUrl(zone, currentPath),\n\t\t};\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client-D_0eAARH.mjs","names":["BasicAuthBoundaryKindValues"],"sources":["../src/schemas.ts","../src/client.ts"],"sourcesContent":["// basic-auth-context-client — config validation schema\n//\n// Uses @standard-schema via the foundation createSchema helper to validate\n// BasicAuthContextClientConfig at the cross-boundary entry point.\n\nimport { createSchema } from \"@securitydept/client\";\nimport type { BasicAuthContextClientConfig } from \"./types\";\n\nfunction isNonEmptyString(v: unknown): v is string {\n\treturn typeof v === \"string\" && v.length > 0;\n}\n\nfunction isObject(v: unknown): v is Record<string, unknown> {\n\treturn typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\nfunction validateZoneConfig(input: unknown, index: number) {\n\tconst issues: Array<{ message: string }> = [];\n\tif (!isObject(input)) {\n\t\tissues.push({ message: `zones[${index}]: expected an object` });\n\t\treturn { issues };\n\t}\n\tif (!isNonEmptyString(input.zonePrefix)) {\n\t\tissues.push({\n\t\t\tmessage: `zones[${index}].zonePrefix: expected a non-empty string`,\n\t\t});\n\t}\n\tif (\n\t\tinput.loginSubpath !== undefined &&\n\t\ttypeof input.loginSubpath !== \"string\"\n\t) {\n\t\tissues.push({\n\t\t\tmessage: `zones[${index}].loginSubpath: expected a string if provided`,\n\t\t});\n\t}\n\tif (\n\t\tinput.logoutSubpath !== undefined &&\n\t\ttypeof input.logoutSubpath !== \"string\"\n\t) {\n\t\tissues.push({\n\t\t\tmessage: `zones[${index}].logoutSubpath: expected a string if provided`,\n\t\t});\n\t}\n\treturn issues.length > 0 ? { issues } : null;\n}\n\n/**\n * Schema for validating BasicAuthContextClientConfig.\n *\n * Validates:\n * - `baseUrl` is a non-empty string\n * - `zones` is a non-empty array of valid zone configs\n * - each zone has a non-empty `zonePrefix`\n * - optional fields have correct types when provided\n */\nexport const BasicAuthContextClientConfigSchema =\n\tcreateSchema<BasicAuthContextClientConfig>({\n\t\tvalidate(input) {\n\t\t\tconst issues: Array<{ message: string }> = [];\n\n\t\t\tif (!isObject(input)) {\n\t\t\t\treturn { issues: [{ message: \"Expected an object\" }] };\n\t\t\t}\n\n\t\t\tif (!isNonEmptyString(input.baseUrl)) {\n\t\t\t\tissues.push({ message: \"baseUrl: expected a non-empty string\" });\n\t\t\t}\n\n\t\t\tif (!Array.isArray(input.zones) || input.zones.length === 0) {\n\t\t\t\tissues.push({\n\t\t\t\t\tmessage: \"zones: expected a non-empty array of zone configs\",\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tfor (let i = 0; i < input.zones.length; i++) {\n\t\t\t\t\tconst zoneResult = validateZoneConfig(input.zones[i], i);\n\t\t\t\t\tif (zoneResult) {\n\t\t\t\t\t\tissues.push(...zoneResult.issues);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\tinput.postAuthRedirectParam !== undefined &&\n\t\t\t\ttypeof input.postAuthRedirectParam !== \"string\"\n\t\t\t) {\n\t\t\t\tissues.push({\n\t\t\t\t\tmessage: \"postAuthRedirectParam: expected a string if provided\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (issues.length > 0) {\n\t\t\t\treturn { issues };\n\t\t\t}\n\n\t\t\treturn { value: input as unknown as BasicAuthContextClientConfig };\n\t\t},\n\t});\n","import { validateWithSchemaSync } from \"@securitydept/client\";\nimport { BasicAuthContextClientConfigSchema } from \"./schemas\";\nimport type {\n\tAuthGuardResult,\n\tBasicAuthBoundaryKind,\n\tBasicAuthBoundaryObservation,\n\tBasicAuthContextClientConfig,\n\tBasicAuthZoneConfig,\n\tResolvedBasicAuthZone,\n} from \"./types\";\nimport {\n\tAuthGuardRedirectStatus,\n\tAuthGuardResultKind,\n\tBasicAuthBoundaryKind as BasicAuthBoundaryKindValues,\n} from \"./types\";\n\nconst DEFAULT_LOGIN_SUBPATH = \"/login\";\nconst DEFAULT_LOGOUT_SUBPATH = \"/logout\";\nconst DEFAULT_POST_AUTH_REDIRECT_PARAM = \"post_auth_redirect_uri\";\n\nfunction resolveZone(config: BasicAuthZoneConfig): ResolvedBasicAuthZone {\n\tconst prefix = config.zonePrefix.replace(/\\/+$/, \"\");\n\tconst loginSub = config.loginSubpath ?? DEFAULT_LOGIN_SUBPATH;\n\tconst logoutSub = config.logoutSubpath ?? DEFAULT_LOGOUT_SUBPATH;\n\n\treturn {\n\t\tzonePrefix: prefix,\n\t\tloginPath: prefix + loginSub,\n\t\tlogoutPath: prefix + logoutSub,\n\t};\n}\n\nexport function readBasicAuthBoundaryKind(\n\toptions: BasicAuthBoundaryObservation,\n): BasicAuthBoundaryKind {\n\tif (options.status < 400) {\n\t\treturn BasicAuthBoundaryKindValues.Authenticated;\n\t}\n\n\tif (options.challengeHeader) {\n\t\treturn BasicAuthBoundaryKindValues.Challenge;\n\t}\n\n\tif (options.status === 401 && options.requestPath === \"/basic/logout\") {\n\t\treturn BasicAuthBoundaryKindValues.LogoutPoison;\n\t}\n\n\treturn BasicAuthBoundaryKindValues.Unauthorized;\n}\n\n/**\n * Basic Auth Context Client.\n *\n * Thin client that manages zone boundary awareness and redirect instructions.\n * It does NOT manage credentials or HTTP auth headers — those are handled\n * by the browser's native Basic Auth UI triggered by 401 + WWW-Authenticate.\n */\nexport class BasicAuthContextClient {\n\treadonly zones: readonly ResolvedBasicAuthZone[];\n\tprivate readonly _baseUrl: string;\n\tprivate readonly _postAuthRedirectParam: string;\n\n\tconstructor(config: BasicAuthContextClientConfig) {\n\t\t// Deprecation-phase validation: warn on invalid config but do not throw.\n\t\t// A future minor release will escalate this to a hard error.\n\t\t// See 110-TS_SDK_MIGRATIONS.md for the full deprecation note.\n\t\tconst result = validateWithSchemaSync(\n\t\t\tBasicAuthContextClientConfigSchema,\n\t\t\tconfig,\n\t\t);\n\t\tif (!result.success) {\n\t\t\tconsole.warn(\n\t\t\t\t`[BasicAuthContextClient] Deprecated config shape detected (will become an error in a future release): ${result.issues.map((i) => i.message).join(\"; \")}`,\n\t\t\t);\n\t\t}\n\n\t\tthis._baseUrl = (config.baseUrl ?? \"\").replace(/\\/+$/, \"\");\n\t\tthis._postAuthRedirectParam =\n\t\t\tconfig.postAuthRedirectParam ?? DEFAULT_POST_AUTH_REDIRECT_PARAM;\n\t\tthis.zones = (config.zones ?? []).map(resolveZone);\n\t}\n\n\t/** Find the zone that contains the given path. */\n\tzoneForPath(path: string): ResolvedBasicAuthZone | undefined {\n\t\tlet matchedZone: ResolvedBasicAuthZone | undefined;\n\n\t\tfor (const zone of this.zones) {\n\t\t\tconst matches =\n\t\t\t\tpath === zone.zonePrefix || path.startsWith(`${zone.zonePrefix}/`);\n\t\t\tif (!matches) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\t!matchedZone ||\n\t\t\t\tzone.zonePrefix.length > matchedZone.zonePrefix.length\n\t\t\t) {\n\t\t\t\tmatchedZone = zone;\n\t\t\t}\n\t\t}\n\n\t\treturn matchedZone;\n\t}\n\n\tzoneForPrefix(zonePrefix: string): ResolvedBasicAuthZone | undefined {\n\t\treturn this.zones.find((zone) => zone.zonePrefix === zonePrefix);\n\t}\n\n\t/** Check whether a path falls inside any configured zone. */\n\tisInZone(path: string): boolean {\n\t\treturn this.zoneForPath(path) !== undefined;\n\t}\n\n\t/** Build the full login URL for a zone, optionally with a post-auth redirect. */\n\tloginUrl(zone: ResolvedBasicAuthZone, postAuthRedirectUri?: string): string {\n\t\tconst base = this._baseUrl + zone.loginPath;\n\t\tif (postAuthRedirectUri) {\n\t\t\tconst params = new URLSearchParams({\n\t\t\t\t[this._postAuthRedirectParam]: postAuthRedirectUri,\n\t\t\t});\n\t\t\treturn `${base}?${params.toString()}`;\n\t\t}\n\t\treturn base;\n\t}\n\n\tloginUrlForZonePrefix(\n\t\tzonePrefix: string,\n\t\tpostAuthRedirectUri?: string,\n\t): string | null {\n\t\tconst zone = this.zoneForPrefix(zonePrefix);\n\t\treturn zone ? this.loginUrl(zone, postAuthRedirectUri) : null;\n\t}\n\n\t/** Build the full logout URL for a zone. */\n\tlogoutUrl(zone: ResolvedBasicAuthZone): string {\n\t\treturn this._baseUrl + zone.logoutPath;\n\t}\n\n\t/**\n\t * Handle a 401 response: if the current path is inside a zone,\n\t * return a redirect instruction to the zone's login URL.\n\t */\n\thandleUnauthorized(\n\t\tcurrentPath: string,\n\t\tresponseStatus: number,\n\t): AuthGuardResult<null> {\n\t\tif (responseStatus !== 401) {\n\t\t\treturn { kind: AuthGuardResultKind.Ok, value: null };\n\t\t}\n\n\t\tconst zone = this.zoneForPath(currentPath);\n\t\tif (!zone) {\n\t\t\treturn { kind: AuthGuardResultKind.Ok, value: null };\n\t\t}\n\n\t\treturn {\n\t\t\tkind: AuthGuardResultKind.Redirect,\n\t\t\tstatus: AuthGuardRedirectStatus.Found,\n\t\t\tlocation: this.loginUrl(zone, currentPath),\n\t\t};\n\t}\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/server/helpers.ts"],"sourcesContent":["// Server-host helpers for basic-auth-context-client\n//\n// Provides host-neutral server helpers that wrap BasicAuthContextClient\n// for SSR / server-render-host scenarios (Next.js, Remix, Astro,\n// plain Node request handlers, etc.).\n//\n// The host owns HTTP response construction (302 headers, response body).\n// These helpers produce redirect instructions the host can act on.\n\nimport { BasicAuthContextClient } from \"../client\";\nimport type { AuthGuardResult, BasicAuthContextClientConfig } from \"../types\";\nimport { AuthGuardResultKind } from \"../types\";\n\n// ---------------------------------------------------------------------------\n// Server request context\n// ---------------------------------------------------------------------------\n\n/** Minimal server request context — host-neutral. */\nexport interface ServerRequestContext {\n\t/** The request path (e.g. \"/api/protected\"). */\n\tpath: string;\n}\n\n// ---------------------------------------------------------------------------\n// Redirect instruction\n// ---------------------------------------------------------------------------\n\n/**\n * Server-side redirect instruction for the host to act on.\n *\n * The host translates this into its framework-specific response:\n * - Next.js: `{ redirect: { destination, statusCode } }`\n * - Express: `res.redirect(statusCode, destination)`\n * - etc.\n */\nexport interface ServerRedirectInstruction {\n\t/** HTTP redirect status code. */\n\tstatusCode: number;\n\t/** Redirect target URL. */\n\tdestination: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helper options\n// ---------------------------------------------------------------------------\n\n/** Options for {@link createBasicAuthServerHelper}. */\nexport interface CreateBasicAuthServerHelperOptions {\n\t/** BasicAuthContextClient config — same shape as the browser client. */\n\tconfig: BasicAuthContextClientConfig;\n}\n\n// ---------------------------------------------------------------------------\n// Helper interface\n// ---------------------------------------------------------------------------\n\n/** Server-host helper for basic-auth zone-based redirect logic. */\nexport interface BasicAuthServerHelper {\n\t/** The underlying BasicAuthContextClient instance. */\n\treadonly client: BasicAuthContextClient;\n\n\t/**\n\t * Handle a 401 response from the backend in a server context.\n\t *\n\t * If the request path falls inside a configured zone, returns a\n\t * redirect instruction. Otherwise returns `null`.\n\t */\n\thandleUnauthorized(\n\t\tcontext: ServerRequestContext,\n\t): ServerRedirectInstruction | null;\n\n\t/**\n\t * Build the login URL for a specific path.\n\t *\n\t * Returns the login URL for the zone containing the path,\n\t * or `null` if the path isn't in any zone.\n\t */\n\tloginUrlForPath(path: string): string | null;\n\n\t/**\n\t * Build the logout URL for a specific path.\n\t *\n\t * Returns the logout URL for the zone containing the path,\n\t * or `null` if the path isn't in any zone.\n\t */\n\tlogoutUrlForPath(path: string): string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a server-host helper for basic-auth zone management.\n *\n * @example\n * ```ts\n * const helper = createBasicAuthServerHelper({\n * config: { baseUrl: \"https://auth.example.com\", zones: [{ zonePrefix: \"/api\" }] },\n * });\n *\n * // In a server request handler:\n * const redirect = helper.handleUnauthorized({ path: \"/api/data\" });\n * if (redirect) {\n * return Response.redirect(redirect.destination, redirect.statusCode);\n * }\n * ```\n */\nexport function createBasicAuthServerHelper(\n\toptions: CreateBasicAuthServerHelperOptions,\n): BasicAuthServerHelper {\n\tconst client = new BasicAuthContextClient(options.config);\n\n\treturn {\n\t\tclient,\n\n\t\thandleUnauthorized(\n\t\t\tcontext: ServerRequestContext,\n\t\t): ServerRedirectInstruction | null {\n\t\t\tconst result: AuthGuardResult<null> = client.handleUnauthorized(\n\t\t\t\tcontext.path,\n\t\t\t\t401,\n\t\t\t);\n\n\t\t\tif (result.kind === AuthGuardResultKind.Redirect) {\n\t\t\t\treturn {\n\t\t\t\t\tstatusCode: result.status,\n\t\t\t\t\tdestination: result.location,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn null;\n\t\t},\n\n\t\tloginUrlForPath(path: string): string | null {\n\t\t\tconst zone = client.zoneForPath(path);\n\t\t\tif (!zone) return null;\n\t\t\treturn client.loginUrl(zone, path);\n\t\t},\n\n\t\tlogoutUrlForPath(path: string): string | null {\n\t\t\tconst zone = client.zoneForPath(path);\n\t\t\tif (!zone) return null;\n\t\t\treturn client.logoutUrl(zone);\n\t\t},\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA4GA,SAAgB,4BACf,SACwB;CACxB,MAAM,SAAS,IAAI,uBAAuB,QAAQ,OAAO;AAEzD,QAAO;EACN;EAEA,mBACC,SACmC;GACnC,MAAM,SAAgC,OAAO,mBAC5C,QAAQ,MACR,IACA;AAED,OAAI,OAAO,SAAS,oBAAoB,SACvC,QAAO;IACN,YAAY,OAAO;IACnB,aAAa,OAAO;IACpB;AAGF,UAAO;;EAGR,gBAAgB,MAA6B;GAC5C,MAAM,OAAO,OAAO,YAAY,KAAK;AACrC,OAAI,CAAC,KAAM,QAAO;AAClB,UAAO,OAAO,SAAS,MAAM,KAAK;;EAGnC,iBAAiB,MAA6B;GAC7C,MAAM,OAAO,OAAO,YAAY,KAAK;AACrC,OAAI,CAAC,KAAM,QAAO;AAClB,UAAO,OAAO,UAAU,KAAK;;EAE9B"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/server/helpers.ts"],"sourcesContent":["// Server-host helpers for basic-auth-context-client\n//\n// Provides host-neutral server helpers that wrap BasicAuthContextClient\n// for SSR / server-render-host scenarios (Next.js, Remix, Astro,\n// plain Node request handlers, etc.).\n//\n// The host owns HTTP response construction (302 headers, response body).\n// These helpers produce redirect instructions the host can act on.\n\nimport { BasicAuthContextClient } from \"../client\";\nimport type { AuthGuardResult, BasicAuthContextClientConfig } from \"../types\";\nimport { AuthGuardResultKind } from \"../types\";\n\n// ---------------------------------------------------------------------------\n// Server request context\n// ---------------------------------------------------------------------------\n\n/** Minimal server request context — host-neutral. */\nexport interface ServerRequestContext {\n\t/** The request path (e.g. \"/api/protected\"). */\n\tpath: string;\n}\n\n// ---------------------------------------------------------------------------\n// Redirect instruction\n// ---------------------------------------------------------------------------\n\n/**\n * Server-side redirect instruction for the host to act on.\n *\n * The host translates this into its framework-specific response:\n * - Next.js: `{ redirect: { destination, statusCode } }`\n * - Express: `res.redirect(statusCode, destination)`\n * - etc.\n */\nexport interface ServerRedirectInstruction {\n\t/** HTTP redirect status code. */\n\tstatusCode: number;\n\t/** Redirect target URL. */\n\tdestination: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helper options\n// ---------------------------------------------------------------------------\n\n/** Options for {@link createBasicAuthServerHelper}. */\nexport interface CreateBasicAuthServerHelperOptions {\n\t/** BasicAuthContextClient config — same shape as the browser client. */\n\tconfig: BasicAuthContextClientConfig;\n}\n\n// ---------------------------------------------------------------------------\n// Helper interface\n// ---------------------------------------------------------------------------\n\n/** Server-host helper for basic-auth zone-based redirect logic. */\nexport interface BasicAuthServerHelper {\n\t/** The underlying BasicAuthContextClient instance. */\n\treadonly client: BasicAuthContextClient;\n\n\t/**\n\t * Handle a 401 response from the backend in a server context.\n\t *\n\t * If the request path falls inside a configured zone, returns a\n\t * redirect instruction. Otherwise returns `null`.\n\t */\n\thandleUnauthorized(\n\t\tcontext: ServerRequestContext,\n\t): ServerRedirectInstruction | null;\n\n\t/**\n\t * Build the login URL for a specific path.\n\t *\n\t * Returns the login URL for the zone containing the path,\n\t * or `null` if the path isn't in any zone.\n\t */\n\tloginUrlForPath(path: string): string | null;\n\n\t/**\n\t * Build the logout URL for a specific path.\n\t *\n\t * Returns the logout URL for the zone containing the path,\n\t * or `null` if the path isn't in any zone.\n\t */\n\tlogoutUrlForPath(path: string): string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a server-host helper for basic-auth zone management.\n *\n * @example\n * ```ts\n * const helper = createBasicAuthServerHelper({\n * config: { baseUrl: \"https://auth.example.com\", zones: [{ zonePrefix: \"/api\" }] },\n * });\n *\n * // In a server request handler:\n * const redirect = helper.handleUnauthorized({ path: \"/api/data\" });\n * if (redirect) {\n * return Response.redirect(redirect.destination, redirect.statusCode);\n * }\n * ```\n */\nexport function createBasicAuthServerHelper(\n\toptions: CreateBasicAuthServerHelperOptions,\n): BasicAuthServerHelper {\n\tconst client = new BasicAuthContextClient(options.config);\n\n\treturn {\n\t\tclient,\n\n\t\thandleUnauthorized(\n\t\t\tcontext: ServerRequestContext,\n\t\t): ServerRedirectInstruction | null {\n\t\t\tconst result: AuthGuardResult<null> = client.handleUnauthorized(\n\t\t\t\tcontext.path,\n\t\t\t\t401,\n\t\t\t);\n\n\t\t\tif (result.kind === AuthGuardResultKind.Redirect) {\n\t\t\t\treturn {\n\t\t\t\t\tstatusCode: result.status,\n\t\t\t\t\tdestination: result.location,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn null;\n\t\t},\n\n\t\tloginUrlForPath(path: string): string | null {\n\t\t\tconst zone = client.zoneForPath(path);\n\t\t\tif (!zone) return null;\n\t\t\treturn client.loginUrl(zone, path);\n\t\t},\n\n\t\tlogoutUrlForPath(path: string): string | null {\n\t\t\tconst zone = client.zoneForPath(path);\n\t\t\tif (!zone) return null;\n\t\t\treturn client.logoutUrl(zone);\n\t\t},\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA4GA,SAAgB,4BACf,SACwB;CACxB,MAAM,SAAS,IAAI,uBAAuB,QAAQ,OAAO;CAEzD,OAAO;EACN;EAEA,mBACC,SACmC;GACnC,MAAM,SAAgC,OAAO,mBAC5C,QAAQ,MACR,IACA;GAED,IAAI,OAAO,SAAS,oBAAoB,UACvC,OAAO;IACN,YAAY,OAAO;IACnB,aAAa,OAAO;IACpB;GAGF,OAAO;;EAGR,gBAAgB,MAA6B;GAC5C,MAAM,OAAO,OAAO,YAAY,KAAK;GACrC,IAAI,CAAC,MAAM,OAAO;GAClB,OAAO,OAAO,SAAS,MAAM,KAAK;;EAGnC,iBAAiB,MAA6B;GAC7C,MAAM,OAAO,OAAO,YAAY,KAAK;GACrC,IAAI,CAAC,MAAM,OAAO;GAClB,OAAO,OAAO,UAAU,KAAK;;EAE9B"}
@@ -1,20 +1,22 @@
1
1
  import { i as AuthGuardResult, t as BasicAuthContextClient } from "../client-DU98lpa_.mjs";
2
+ import { PageLocationCapability } from "@securitydept/client";
2
3
 
3
4
  //#region src/web/login-redirect.d.ts
4
5
  /**
5
6
  * Options for {@link loginWithRedirect}.
6
7
  */
7
8
  interface LoginWithRedirectOptions {
9
+ environment?: PageLocationCapability;
8
10
  /**
9
11
  * The current request path to resolve the target zone.
10
12
  *
11
- * When omitted, `window.location.pathname` is used.
13
+ * When omitted, `environment.location.pathname` is used.
12
14
  */
13
15
  currentPath?: string;
14
16
  /**
15
17
  * Where to redirect the user after successful authentication.
16
18
  *
17
- * When omitted, `window.location.href` is used as the return URI.
19
+ * When omitted, `environment.location.href` is used as the return URI.
18
20
  */
19
21
  postAuthRedirectUri?: string;
20
22
  }
@@ -36,10 +38,13 @@ interface LoginWithRedirectOptions {
36
38
  declare function loginWithRedirect(client: BasicAuthContextClient, options?: LoginWithRedirectOptions): boolean;
37
39
  //#endregion
38
40
  //#region src/web/redirect.d.ts
41
+ interface PerformRedirectOptions {
42
+ environment?: PageLocationCapability;
43
+ }
39
44
  /**
40
45
  * Perform a browser redirect for an `AuthGuardResult` that requires redirection.
41
46
  */
42
- declare function performRedirect(result: AuthGuardResult<unknown>): void;
47
+ declare function performRedirect(result: AuthGuardResult<unknown>, options?: PerformRedirectOptions): void;
43
48
  //#endregion
44
- export { type LoginWithRedirectOptions, loginWithRedirect, performRedirect };
49
+ export { type LoginWithRedirectOptions, type PerformRedirectOptions, loginWithRedirect, performRedirect };
45
50
  //# sourceMappingURL=index.d.mts.map
@@ -1,5 +1,7 @@
1
1
  import { n as AuthGuardResultKind } from "../types-2IoKrgnl.mjs";
2
+ import { assertResolveEnvironment } from "@securitydept/client/web";
2
3
  //#region src/web/login-redirect.ts
4
+ const BASIC_AUTH_PAGE_ENVIRONMENT_ERROR_MESSAGE$1 = "basic-auth browser redirect helpers require an explicit page environment.\nCreate one in your composition root with createBrowserPageClientEnvironment(...).";
3
5
  /**
4
6
  * One-shot browser redirect to the zone login endpoint for the given path.
5
7
  *
@@ -16,20 +18,31 @@ import { n as AuthGuardResultKind } from "../types-2IoKrgnl.mjs";
16
18
  * of the host-neutral client contract.
17
19
  */
18
20
  function loginWithRedirect(client, options = {}) {
19
- const currentPath = options.currentPath ?? window.location.pathname;
21
+ const environment = assertResolveEnvironment(options.environment, failMissingPageEnvironment$1);
22
+ const currentPath = options.currentPath ?? environment.location.pathname ?? new URL(environment.location.href).pathname;
20
23
  const zone = client.zoneForPath(currentPath);
21
24
  if (!zone) return false;
22
- const postAuthRedirectUri = options.postAuthRedirectUri ?? window.location.href;
23
- window.location.href = client.loginUrl(zone, postAuthRedirectUri);
25
+ const postAuthRedirectUri = options.postAuthRedirectUri ?? environment.location.href;
26
+ environment.location.href = client.loginUrl(zone, postAuthRedirectUri);
24
27
  return true;
25
28
  }
29
+ function failMissingPageEnvironment$1() {
30
+ throw new Error(BASIC_AUTH_PAGE_ENVIRONMENT_ERROR_MESSAGE$1);
31
+ }
26
32
  //#endregion
27
33
  //#region src/web/redirect.ts
34
+ const BASIC_AUTH_PAGE_ENVIRONMENT_ERROR_MESSAGE = "basic-auth browser redirect helpers require an explicit page environment.\nCreate one in your composition root with createBrowserPageClientEnvironment(...).";
28
35
  /**
29
36
  * Perform a browser redirect for an `AuthGuardResult` that requires redirection.
30
37
  */
31
- function performRedirect(result) {
32
- if (result.kind === AuthGuardResultKind.Redirect) globalThis.location.href = result.location;
38
+ function performRedirect(result, options = {}) {
39
+ if (result.kind === AuthGuardResultKind.Redirect) {
40
+ const environment = assertResolveEnvironment(options.environment, failMissingPageEnvironment);
41
+ environment.location.href = result.location;
42
+ }
43
+ }
44
+ function failMissingPageEnvironment() {
45
+ throw new Error(BASIC_AUTH_PAGE_ENVIRONMENT_ERROR_MESSAGE);
33
46
  }
34
47
  //#endregion
35
48
  export { loginWithRedirect, performRedirect };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/web/login-redirect.ts","../../src/web/redirect.ts"],"sourcesContent":["// Basic Auth Context Client — Browser login redirect convenience\n//\n// Provides a one-shot browser redirect to a zone's login page, matching the\n// convenience pattern established by session-context-client/web.\n//\n// Unlike session-context where there is a single login URL, basic-auth is\n// zone-based: the adopter must identify which zone to target. This helper\n// resolves the zone from a path and performs the redirect.\n//\n// Stability: provisional\n\nimport type { BasicAuthContextClient } from \"../client\";\n\n/**\n * Options for {@link loginWithRedirect}.\n */\nexport interface LoginWithRedirectOptions {\n\t/**\n\t * The current request path to resolve the target zone.\n\t *\n\t * When omitted, `window.location.pathname` is used.\n\t */\n\tcurrentPath?: string;\n\n\t/**\n\t * Where to redirect the user after successful authentication.\n\t *\n\t * When omitted, `window.location.href` is used as the return URI.\n\t */\n\tpostAuthRedirectUri?: string;\n}\n\n/**\n * One-shot browser redirect to the zone login endpoint for the given path.\n *\n * 1. Resolves the zone for the current (or specified) path.\n * 2. Navigates the current window to the zone's login URL with a\n * post-auth redirect parameter.\n *\n * Returns `false` if no zone matches the path (no redirect performed).\n * Returns `true` if the redirect was initiated.\n *\n * This is the recommended browser entry point for initiating basic-auth\n * login. It is intentionally a standalone function (not a method on\n * `BasicAuthContextClient`) to keep browser-specific side-effects out\n * of the host-neutral client contract.\n */\nexport function loginWithRedirect(\n\tclient: BasicAuthContextClient,\n\toptions: LoginWithRedirectOptions = {},\n): boolean {\n\tconst currentPath = options.currentPath ?? window.location.pathname;\n\tconst zone = client.zoneForPath(currentPath);\n\tif (!zone) {\n\t\treturn false;\n\t}\n\n\tconst postAuthRedirectUri =\n\t\toptions.postAuthRedirectUri ?? window.location.href;\n\twindow.location.href = client.loginUrl(zone, postAuthRedirectUri);\n\treturn true;\n}\n","import type { AuthGuardResult } from \"../types\";\nimport { AuthGuardResultKind } from \"../types\";\n\n/**\n * Perform a browser redirect for an `AuthGuardResult` that requires redirection.\n */\nexport function performRedirect(result: AuthGuardResult<unknown>): void {\n\tif (result.kind === AuthGuardResultKind.Redirect) {\n\t\t// Accessing location via window to satisfy strict type checks.\n\t\t(globalThis as unknown as { location: { href: string } }).location.href =\n\t\t\tresult.location;\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA+CA,SAAgB,kBACf,QACA,UAAoC,EAAE,EAC5B;CACV,MAAM,cAAc,QAAQ,eAAe,OAAO,SAAS;CAC3D,MAAM,OAAO,OAAO,YAAY,YAAY;AAC5C,KAAI,CAAC,KACJ,QAAO;CAGR,MAAM,sBACL,QAAQ,uBAAuB,OAAO,SAAS;AAChD,QAAO,SAAS,OAAO,OAAO,SAAS,MAAM,oBAAoB;AACjE,QAAO;;;;;;;ACtDR,SAAgB,gBAAgB,QAAwC;AACvE,KAAI,OAAO,SAAS,oBAAoB,SAEtC,YAAyD,SAAS,OAClE,OAAO"}
1
+ {"version":3,"file":"index.mjs","names":["BASIC_AUTH_PAGE_ENVIRONMENT_ERROR_MESSAGE","failMissingPageEnvironment"],"sources":["../../src/web/login-redirect.ts","../../src/web/redirect.ts"],"sourcesContent":["// Basic Auth Context Client — Browser login redirect convenience\n//\n// Provides a one-shot browser redirect to a zone's login page, matching the\n// convenience pattern established by session-context-client/web.\n//\n// Unlike session-context where there is a single login URL, basic-auth is\n// zone-based: the adopter must identify which zone to target. This helper\n// resolves the zone from a path and performs the redirect.\n//\n// Stability: provisional\n\nimport type { PageLocationCapability } from \"@securitydept/client\";\nimport { assertResolveEnvironment } from \"@securitydept/client/web\";\nimport type { BasicAuthContextClient } from \"../client\";\n\nconst BASIC_AUTH_PAGE_ENVIRONMENT_ERROR_MESSAGE =\n\t\"basic-auth browser redirect helpers require an explicit page environment.\\n\" +\n\t\"Create one in your composition root with createBrowserPageClientEnvironment(...).\";\n\n/**\n * Options for {@link loginWithRedirect}.\n */\nexport interface LoginWithRedirectOptions {\n\tenvironment?: PageLocationCapability;\n\n\t/**\n\t * The current request path to resolve the target zone.\n\t *\n\t * When omitted, `environment.location.pathname` is used.\n\t */\n\tcurrentPath?: string;\n\n\t/**\n\t * Where to redirect the user after successful authentication.\n\t *\n\t * When omitted, `environment.location.href` is used as the return URI.\n\t */\n\tpostAuthRedirectUri?: string;\n}\n\n/**\n * One-shot browser redirect to the zone login endpoint for the given path.\n *\n * 1. Resolves the zone for the current (or specified) path.\n * 2. Navigates the current window to the zone's login URL with a\n * post-auth redirect parameter.\n *\n * Returns `false` if no zone matches the path (no redirect performed).\n * Returns `true` if the redirect was initiated.\n *\n * This is the recommended browser entry point for initiating basic-auth\n * login. It is intentionally a standalone function (not a method on\n * `BasicAuthContextClient`) to keep browser-specific side-effects out\n * of the host-neutral client contract.\n */\nexport function loginWithRedirect(\n\tclient: BasicAuthContextClient,\n\toptions: LoginWithRedirectOptions = {},\n): boolean {\n\tconst environment = assertResolveEnvironment(\n\t\toptions.environment,\n\t\tfailMissingPageEnvironment,\n\t);\n\tconst currentPath =\n\t\toptions.currentPath ??\n\t\tenvironment.location.pathname ??\n\t\tnew URL(environment.location.href).pathname;\n\tconst zone = client.zoneForPath(currentPath);\n\tif (!zone) {\n\t\treturn false;\n\t}\n\n\tconst postAuthRedirectUri =\n\t\toptions.postAuthRedirectUri ?? environment.location.href;\n\tenvironment.location.href = client.loginUrl(zone, postAuthRedirectUri);\n\treturn true;\n}\n\nfunction failMissingPageEnvironment(): never {\n\tthrow new Error(BASIC_AUTH_PAGE_ENVIRONMENT_ERROR_MESSAGE);\n}\n","import type { PageLocationCapability } from \"@securitydept/client\";\nimport { assertResolveEnvironment } from \"@securitydept/client/web\";\nimport type { AuthGuardResult } from \"../types\";\nimport { AuthGuardResultKind } from \"../types\";\n\nconst BASIC_AUTH_PAGE_ENVIRONMENT_ERROR_MESSAGE =\n\t\"basic-auth browser redirect helpers require an explicit page environment.\\n\" +\n\t\"Create one in your composition root with createBrowserPageClientEnvironment(...).\";\n\nexport interface PerformRedirectOptions {\n\tenvironment?: PageLocationCapability;\n}\n\n/**\n * Perform a browser redirect for an `AuthGuardResult` that requires redirection.\n */\nexport function performRedirect(\n\tresult: AuthGuardResult<unknown>,\n\toptions: PerformRedirectOptions = {},\n): void {\n\tif (result.kind === AuthGuardResultKind.Redirect) {\n\t\tconst environment = assertResolveEnvironment(\n\t\t\toptions.environment,\n\t\t\tfailMissingPageEnvironment,\n\t\t);\n\t\tenvironment.location.href = result.location;\n\t}\n}\n\nfunction failMissingPageEnvironment(): never {\n\tthrow new Error(BASIC_AUTH_PAGE_ENVIRONMENT_ERROR_MESSAGE);\n}\n"],"mappings":";;;AAeA,MAAMA,8CACL;;;;;;;;;;;;;;;;AAuCD,SAAgB,kBACf,QACA,UAAoC,EAAE,EAC5B;CACV,MAAM,cAAc,yBACnB,QAAQ,aACRC,6BACA;CACD,MAAM,cACL,QAAQ,eACR,YAAY,SAAS,YACrB,IAAI,IAAI,YAAY,SAAS,KAAK,CAAC;CACpC,MAAM,OAAO,OAAO,YAAY,YAAY;CAC5C,IAAI,CAAC,MACJ,OAAO;CAGR,MAAM,sBACL,QAAQ,uBAAuB,YAAY,SAAS;CACrD,YAAY,SAAS,OAAO,OAAO,SAAS,MAAM,oBAAoB;CACtE,OAAO;;AAGR,SAASA,+BAAoC;CAC5C,MAAM,IAAI,MAAMD,4CAA0C;;;;AC1E3D,MAAM,4CACL;;;;AAUD,SAAgB,gBACf,QACA,UAAkC,EAAE,EAC7B;CACP,IAAI,OAAO,SAAS,oBAAoB,UAAU;EACjD,MAAM,cAAc,yBACnB,QAAQ,aACR,2BACA;EACD,YAAY,SAAS,OAAO,OAAO;;;AAIrC,SAAS,6BAAoC;CAC5C,MAAM,IAAI,MAAM,0CAA0C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@securitydept/basic-auth-context-client",
3
- "version": "0.3.0-beta.2",
3
+ "version": "0.3.0-beta.3",
4
4
  "description": "Basic Auth Context Client of the SecurityDept TypeScript SDK, a layered authentication and authorization toolkit built as reusable Rust crates, a TypeScript client SDK workspace, and reference server/web apps.",
5
5
  "author": "Zhou Yeheng <root@aitiotekt.com>",
6
6
  "license": "MIT",
@@ -45,10 +45,10 @@
45
45
  "dist"
46
46
  ],
47
47
  "dependencies": {
48
- "@securitydept/client": "0.3.0-beta.2"
48
+ "@securitydept/client": "0.3.0-beta.3"
49
49
  },
50
50
  "scripts": {
51
51
  "build": "tsdown",
52
- "typecheck": "tsc -b"
52
+ "typecheck": "tsc -b --force"
53
53
  }
54
54
  }