Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/lazy-icons-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@halfdomelabs/baseplate-plugin-storage': patch
'@halfdomelabs/project-builder-common': patch
'@halfdomelabs/project-builder-server': patch
'@halfdomelabs/baseplate-plugin-auth': patch
'@halfdomelabs/project-builder-lib': patch
'@halfdomelabs/project-builder-web': patch
'@halfdomelabs/react-generators': patch
---

Split out auth0 functionality into separate plugin
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"typecheck": "turbo run typecheck --affected",
"versions:check": "check-dependency-version-consistency .",
"versions:fix": "check-dependency-version-consistency . --fix",
"watch": "turbo run watch"
"watch": "turbo run watch --concurrency 20"
},
"dependencies": {
"@changesets/cli": "2.28.1",
Expand Down
2 changes: 0 additions & 2 deletions packages/fastify-generators/src/constants/fastify-packages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ export const FASTIFY_PACKAGES = {

// Auth
'@node-rs/argon2': '2.0.2',
auth0: '4.0.2',
'fastify-auth0-verify': '3.0.0',

// Postmark
postmark: '4.0.2',
Expand Down
1 change: 0 additions & 1 deletion packages/fastify-generators/src/generators/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export * from './auth/index.js';
export * from './auth0/index.js';
export * from './bull/index.js';
export * from './core/index.js';
export * from './email/index.js';
Expand Down
1 change: 1 addition & 0 deletions packages/project-builder-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"prettier:write": "prettier -w ."
},
"dependencies": {
"@halfdomelabs/baseplate-plugin-auth": "workspace:*",
"@halfdomelabs/baseplate-plugin-storage": "workspace:*",
"@halfdomelabs/project-builder-lib": "workspace:*",
"@halfdomelabs/project-builder-server": "workspace:*",
Expand Down
3 changes: 2 additions & 1 deletion packages/project-builder-lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"zustand": "5.0.3"
},
"devDependencies": {
"@faker-js/faker": "9.8.0",
"@halfdomelabs/tools": "workspace:*",
"@types/culori": "^2.1.1",
"@types/node": "catalog:",
Expand All @@ -59,7 +60,7 @@
"eslint": "catalog:",
"prettier": "catalog:",
"tsc-alias": "catalog:",
"type-fest": "4.32.0",
"type-fest": "4.41.0",
"typescript": "catalog:",
"vitest": "catalog:"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ function ensureFeatureByNameRecursively(
return parentRef;
}

function getFeatureByName(
projectDefinition: ProjectDefinition,
name: string,
): FeatureConfig | undefined {
return projectDefinition.features.find((f) => f.name === name);
}

export const FeatureUtils = {
getRootFeatures,
getFeatureById,
Expand All @@ -102,4 +109,5 @@ export const FeatureUtils = {
getFeaturePathById,
validateFeatureName,
ensureFeatureByNameRecursively,
getFeatureByName,
};
1 change: 1 addition & 0 deletions packages/project-builder-lib/src/definition/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './model/model-transformer-utils.js';
export * from './model/model-utils.js';
export * from './plugins/index.js';
export * from './project-definition-container.js';
export * from './project-definition-container.test-helper.js';
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { PASCAL_CASE_REGEX } from '@halfdomelabs/utils';

import type {
ModelConfig,
ModelRelationFieldConfig,
Expand Down Expand Up @@ -62,10 +64,15 @@ function hasService(model: ModelConfig): boolean {
return (
!!model.service.create.enabled ||
!!model.service.update.enabled ||
!!model.service.delete.enabled
!!model.service.delete.enabled ||
model.service.transformers.length > 0
);
}

function validateModelName(name: string): boolean {
return PASCAL_CASE_REGEX.test(name);
}

export const ModelUtils = {
byId,
byIdOrThrow,
Expand All @@ -74,4 +81,5 @@ export const ModelUtils = {
getModelsForFeature,
getModelIdFields,
hasService,
validateModelName,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { getLatestMigrationVersion } from '@src/migrations/index.js';
import {
PluginImplementationStore,
zPluginWrapper,
} from '@src/plugins/index.js';
import { deserializeSchemaWithReferences } from '@src/references/deserialize-schema.js';
import {
type ProjectDefinition,
projectDefinitionSchema,
} from '@src/schema/project-definition.js';

import { ProjectDefinitionContainer } from './project-definition-container.js';

export function createTestProjectDefinition(
input: Partial<ProjectDefinition> = {},
): ProjectDefinition {
return {
name: 'test-project',
features: [],
version: '1.0.0',
cliVersion: '1.0.0',
portOffset: 3000,
apps: [],
models: [],
isInitialized: true,
schemaVersion: getLatestMigrationVersion(),
...input,
};
}

export function createTestProjectDefinitionContainer(
input: Partial<ProjectDefinition> = {},
): ProjectDefinitionContainer {
const pluginStore = {
availablePlugins: [],
};
const pluginImplementationStore = new PluginImplementationStore({});
const schemaWithPlugins = zPluginWrapper(
projectDefinitionSchema,
pluginImplementationStore,
);
const resolvedRefPayload = deserializeSchemaWithReferences(
schemaWithPlugins,
createTestProjectDefinition(input),
);
return new ProjectDefinitionContainer(
resolvedRefPayload,
{ pluginStore },
pluginImplementationStore,
);
}
3 changes: 2 additions & 1 deletion packages/project-builder-lib/src/migrations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { migration008AnonymousPublicRole } from './migration-008-anonymous-publi
import { migration009RenameRefs } from './migration-009-rename-refs.js';
import { migration010HexToOklch } from './migration-010-hex-to-oklch.js';
import { migration011PluginId } from './migration-011-plugin-id.js';

import { migration012MigrateAuthConfig } from './migration-012-migrate-auth-config.js';
export const SCHEMA_MIGRATIONS: SchemaMigration[] = [
migration005PrimaryUniqueRefs,
migration006IndividualServiceControllers,
Expand All @@ -17,6 +17,7 @@ export const SCHEMA_MIGRATIONS: SchemaMigration[] = [
migration009RenameRefs,
migration010HexToOklch,
migration011PluginId,
migration012MigrateAuthConfig,
];

export function isMigrateableProjectDefinition(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { createSchemaMigration } from './types.js';

interface OldConfig {
auth?: {
userModelRef: string;
userRoleModelRef: string;
useAuth0: boolean;
authFeatureRef: string;
accountsFeatureRef: string;
passwordProvider?: boolean;
roles: {
id: string;
name: string;
comment: string;
builtIn: boolean;
}[];
};
plugins?: {
id: string;
name: string;
packageName: string;
version: string;
config?: unknown;
}[];
}

interface NewConfig {
auth?: undefined;
plugins?: {
id: string;
name: string;
packageName: string;
version: string;
config?: unknown;
}[];
}

export const migration012MigrateAuthConfig = createSchemaMigration<
OldConfig,
NewConfig
>({
version: 12,
name: 'migrateAuthConfig',
description: 'Migrate auth config to auth0 plugin config',
migrate: (config) => {
if (!config.auth?.useAuth0) {
return {
...config,
auth: undefined,
};
}
Comment on lines +45 to +51

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Prefer deleting the legacy auth key instead of assigning undefined

Assigning undefined leaves the key present on the object, which can trip
hasOwnProperty checks and other truthy evaluations.
A safer pattern is:

-      return {
-        ...config,
-        auth: undefined,
-      };
+      // clone to avoid mutating the original config
+      const { auth: _unused, ...rest } = config;
+      return rest;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
migrate: (config) => {
if (!config.auth?.useAuth0) {
return {
...config,
auth: undefined,
};
}
migrate: (config) => {
if (!config.auth?.useAuth0) {
// clone to avoid mutating the original config
const { auth: _unused, ...rest } = config;
return rest;
}
🤖 Prompt for AI Agents
In
packages/project-builder-lib/src/migrations/migration-012-migrate-auth-config.ts
around lines 45 to 51, instead of setting the auth key to undefined when
useAuth0 is false, remove the auth key entirely from the config object. This can
be done by creating a shallow copy of config and deleting the auth property from
that copy before returning it, ensuring the auth key is not present at all.


const auth0PluginId = 'plugin:halfdomelabs_baseplate-plugin-auth_auth0';

const auth0Config = {
userAccountModelRef: config.auth.userModelRef,
authFeatureRef: config.auth.authFeatureRef,
roles: config.auth.roles.map((role) => ({
id: role.id,
name: role.name,
comment: role.comment,
builtIn: role.builtIn,
})),
};

const plugins = config.plugins ?? [];
const pluginIndex = plugins.findIndex((p) => p.id === auth0PluginId);

if (pluginIndex === -1) {
plugins.push({
id: auth0PluginId,
name: 'auth0',
packageName: '@halfdomelabs/baseplate-plugin-auth',
version: '0.1.0',
config: auth0Config,
});
} else {
plugins[pluginIndex] = {
...plugins[pluginIndex],
config: auth0Config,
};
}
Comment on lines +66 to +82

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid mutating the original plugins array

const plugins = config.plugins ?? [] keeps a reference to the original array.
Pushing into it means callers holding the previous config may observe
unexpected in-place mutations.

-const plugins = config.plugins ?? [];
+const plugins = [...(config.plugins ?? [])];

This copy costs virtually nothing but guarantees immutability within the
migration pipeline.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const plugins = config.plugins ?? [];
const pluginIndex = plugins.findIndex((p) => p.id === auth0PluginId);
if (pluginIndex === -1) {
plugins.push({
id: auth0PluginId,
name: 'auth0',
packageName: '@halfdomelabs/baseplate-plugin-auth',
version: '0.1.0',
config: auth0Config,
});
} else {
plugins[pluginIndex] = {
...plugins[pluginIndex],
config: auth0Config,
};
}
const plugins = [...(config.plugins ?? [])];
const pluginIndex = plugins.findIndex((p) => p.id === auth0PluginId);
if (pluginIndex === -1) {
plugins.push({
id: auth0PluginId,
name: 'auth0',
packageName: '@halfdomelabs/baseplate-plugin-auth',
version: '0.1.0',
config: auth0Config,
});
} else {
plugins[pluginIndex] = {
...plugins[pluginIndex],
config: auth0Config,
};
}
🤖 Prompt for AI Agents
In
packages/project-builder-lib/src/migrations/migration-012-migrate-auth-config.ts
around lines 66 to 82, the code mutates the original plugins array by pushing or
updating elements directly, which can cause unexpected side effects. To fix
this, create a shallow copy of the plugins array before modifying it, for
example by using array spread syntax, and then perform push or update operations
on this new array to ensure immutability of the original config.plugins.


return {
...config,
auth: undefined,
plugins,
};
},
});
6 changes: 6 additions & 0 deletions packages/project-builder-lib/src/plugins/schema/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,10 @@ export class PluginImplementationStore {

return implementation;
}

getPluginSpecOptional<TImplementation>(
spec: PluginSpec<TImplementation>,
): TImplementation | undefined {
return this.implementations[spec.name] as TImplementation | undefined;
}
}
25 changes: 25 additions & 0 deletions packages/project-builder-lib/src/plugins/spec/auth-config-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { AuthRole } from '@src/schema/index.js';
import type { ProjectDefinition } from '@src/schema/project-definition.js';

import type { PluginSpecImplementation } from './types.js';

import { createPluginSpec } from './types.js';

type UserAccountModelGetter = (definition: ProjectDefinition) => string;
type AuthRolesGetter = (definition: ProjectDefinition) => AuthRole[];

/**
* Spec for allowing plugins to declare standard auth configurations
*/
export interface AuthConfigSpec extends PluginSpecImplementation {
getUserAccountModel: UserAccountModelGetter;
getAuthRoles: AuthRolesGetter;
}

/**
* Spec for adding config component for plugin
*/
export const authConfigSpec = createPluginSpec<AuthConfigSpec>(
'core/auth-config',
{},
);
1 change: 1 addition & 0 deletions packages/project-builder-lib/src/plugins/spec/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './auth-config-spec.js';
export * from './config-spec.js';
export * from './types.js';
export * from './web-config-spec.js';
2 changes: 1 addition & 1 deletion packages/project-builder-lib/src/schema/apps/admin/app.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { z } from 'zod';

import { zRef, zRefBuilder } from '@src/references/index.js';
import { authRoleEntityType } from '@src/schema/auth/types.js';
import { authRoleEntityType } from '@src/schema/auth/index.js';

import type { AdminCrudSectionConfig } from './sections/index.js';

Expand Down
2 changes: 1 addition & 1 deletion packages/project-builder-lib/src/schema/apps/web/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { z } from 'zod';

import { zRef } from '@src/references/index.js';
import { authRoleEntityType } from '@src/schema/auth/types.js';
import { authRoleEntityType } from '@src/schema/auth/index.js';

import { baseAppValidators } from '../base.js';
import { createAppEntryType } from '../types.js';
Expand Down
Loading