Skip to content

feat: extract Azure Functions bootstrap into @cellix/api-core - #334

Open
noce-nick wants to merge 3 commits into
mainfrom
feat/cellix-api-core
Open

noce-nick wants to merge 3 commits into
mainfrom
feat/cellix-api-core

Conversation

@noce-nick

@noce-nick noce-nick commented Sep 18, 2026

Copy link
Copy Markdown
Member

Summary

  • Extracts the inlined Cellix Azure Functions bootstrap from apps/api into a new framework package, @cellix/api-core.
  • Keeps this as API-host bootstrap (core as in startup), not a generic @cellix/core and not a counterpart to @cellix/ui-core.
  • @apps/api now consumes Cellix from the package. Handler names, routes, service registration, and appStart / appTerminate timing are unchanged.

The fluent consumer chain is still:

Cellix.initializeInfrastructureServices(...)
  .setContext(...)
  .initializeApplicationServices(...)
  .registerAzureFunctionHttpHandler(...)
  .startUp();

startUp() still only binds Azure Functions handlers and lifecycle hooks. Infrastructure startUp(), context creation, and the application host still run later in appStart.

Public contract

Runtime export: Cellix

Signature types: InfrastructureServiceRegistry, InitializedServiceRegistry, ContextBuilder, ApplicationServicesInitializer, AzureFunctionHandlerRegistry, StartedApplication, AppHost, ServiceKey

Internal: phase machine details, pending handler records, InfrastructureServiceStore.

This is a new package, so the public surface is the main review target.

Tests

Contract tests import @cellix/api-core only (packages/cellix/api-core/tests/cellix.test.ts), grouped under Cellix. They cover registration, phase errors, named vs constructor lookup, lifecycle success/failure (including non-Error rejections), and HTTP handler execution before and after appStart.

The old application-local cucumber suite was removed instead of duplicated; it imported ./cellix.ts and poked private fields.

Validation

Passed:

  • @cellix/api-core build, lint, tests (62), coverage (98.33% lines / 92.85% branches)
  • @apps/api build and tests
  • @cellix/archunit-tests
  • knip (apps/api still depends on @azure/functions for the Functions host; knip now ignores that unused-import finding)
  • cellix-tdd evaluator 20/20
  • pre-commit verify through format, arch, coverage merge, e2e (24 scenarios), knip, and audit

Snyk:

  • Targeted snyk test on @cellix/api-core: no vulnerable paths
  • snyk code on packages/cellix/api-core and apps/api/src: 0 issues
  • Repo-wide snyk test --all-projects hits the cellixjs org monthly private-test limit, so the pre-commit hook cannot currently complete that step

Review notes

Please focus on:

  1. Whether the exported type list is the right public surface
  2. That API startup behavior is preserved
  3. Whether @cellix/api-core is the name you want going forward

Summary by Sourcery

Extract the Azure Functions API bootstrap into @cellix/api-core and update the API host to consume the reusable package without changing startup behavior.

New Features:

  • Add the new @cellix/api-core package as the reusable Azure Functions bootstrap for Cellix API applications.
  • Expose the Cellix bootstrap facade and its fluent startup contract types from the package root.

Enhancements:

  • Move API bootstrap ownership from application-local code into the framework package while preserving handler registration, routes, service registration, and lifecycle timing.
  • Document the package scope, public contract, startup behavior, and service and handler usage.

Build:

  • Add package build, TypeScript, Vitest, workspace, lockfile, and dependency configuration for @cellix/api-core.

Documentation:

  • Add consumer README and maintainer manifest documenting the API bootstrap contract and package boundaries.

Tests:

  • Replace private application-local bootstrap tests with package-level contract tests covering registration, phase validation, service lookup, lifecycle behavior, and HTTP handler execution.

Chores:

  • Remove the duplicated inlined bootstrap implementation and its application-local feature suite.
  • Update the API application and project analysis configuration to consume and include the new package.

Move the Cellix API host bootstrap out of the application package
into a framework-owned fluent facade so Azure Functions apps can
register infrastructure services, context, and HTTP handlers without
keeping that startup code beside application-specific wiring.
@noce-nick
noce-nick requested a review from a team September 18, 2026 21:16
@noce-nick
noce-nick requested a review from a team as a code owner September 18, 2026 21:16
@sourcery-ai

sourcery-ai Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

This PR extracts the API-host Azure Functions bootstrap into @cellix/api-core, formalizes its public fluent API with package-level contract tests and documentation, and switches apps/api to the shared implementation without changing handler registration or lifecycle timing.

Sequence diagram for the Azure Functions bootstrap lifecycle

sequenceDiagram
    participant Api as apps/api
    participant Cellix as Cellix
    participant Azure as AzureFunctions
    participant Services as InfrastructureServices
    participant Host as AppHost

    Api->>Cellix: initializeInfrastructureServices(registerServices)
    Api->>Cellix: setContext(contextCreator)
    Api->>Cellix: initializeApplicationServices(factory)
    Api->>Cellix: registerAzureFunctionHttpHandler(name, options, handlerCreator)
    Api->>Cellix: startUp()
    Cellix->>Azure: app.http(name, handler)
    Cellix->>Azure: app.hook.appStart(...)
    Cellix->>Azure: app.hook.appTerminate(...)
    Azure->>Cellix: appStart
    Cellix->>Services: startUp()
    Cellix->>Cellix: contextCreator(registry)
    Cellix->>Host: factory(context)
    Azure->>Cellix: HTTP request
    Cellix->>Host: handlerCreator(host, registry)
    Host-->>Azure: request response
    Azure->>Cellix: appTerminate
    Cellix->>Services: shutDown()
Loading

Flow diagram for the Cellix fluent bootstrap phases

flowchart LR
    Infrastructure["Infrastructure registration"] --> Context["setContext"]
    Context --> AppServices["initializeApplicationServices"]
    AppServices --> Handlers["registerAzureFunctionHttpHandler"]
    Handlers --> Started["startUp() binds handlers and lifecycle hooks"]
    Started --> AppStart["Azure Functions appStart"]
    AppStart --> Ready["Services started, context built, AppHost created"]
Loading

File-Level Changes

Change Details Files
Extract the Azure Functions bootstrap into a reusable package with a constrained root export surface.
  • Move the phased Cellix facade and lifecycle orchestration into @cellix/api-core.
  • Expose only Cellix plus fluent-chain signature types; keep phase state, handler records, and service storage internal.
  • Add package metadata, TypeScript/build/test configuration, consumer and maintainer documentation, and contract-focused tests.
packages/cellix/api-core/src/cellix.ts
packages/cellix/api-core/src/types.ts
packages/cellix/api-core/src/infrastructure-service-store.ts
packages/cellix/api-core/src/index.ts
packages/cellix/api-core/package.json
packages/cellix/api-core/README.md
packages/cellix/api-core/manifest.md
packages/cellix/api-core/tests/cellix.test.ts
packages/cellix/api-core/tsconfig.json
packages/cellix/api-core/tsconfig.vitest.json
packages/cellix/api-core/turbo.json
packages/cellix/api-core/vitest.config.ts
packages/cellix/api-core/.gitignore
packages/cellix/api-core/cellix-tdd-summary.md
Update the API host to consume the extracted bootstrap while preserving its existing startup contract.
  • Replace the local Cellix import and dependency/project reference with @cellix/api-core.
  • Retain handler names, routes, service registration, and deferred appStart/appTerminate behavior.
  • Remove the application-local implementation, tests, and cucumber feature; update the API index test mock.
apps/api/package.json
apps/api/tsconfig.json
apps/api/src/index.ts
apps/api/src/index.test.ts
apps/api/src/cellix.ts
apps/api/src/cellix.test.ts
apps/api/src/features/cellix.feature
Integrate the new package into workspace analysis and dependency metadata.
  • Add the package to workspace and lockfile resolution.
  • Include package sources and tests in Sonar analysis.
  • Adjust dependency overrides and Knip configuration for the moved host dependency.
pnpm-lock.yaml
pnpm-workspace.yaml
sonar-project.properties
knip.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="packages/cellix/api-core/src/cellix.ts" line_range="143" />
<code_context>
-	}
-
-	public get context(): ContextType {
-		if (!this.contextInternal) {
-			throw new Error('Context not initialized');
-		}
</code_context>
<issue_to_address>
**issue (bug_risk):** The `context` getter throws `Context not initialized` when a valid context value is falsy, such as `false`, `0`, or an empty string, because initialization is checked with `!this.contextInternal` instead of an undefined check.

**Triggers:** When a consumer uses a falsy `ContextType` value.

**Suggested fix:** Check `this.contextInternal === undefined` rather than relying on truthiness.

```suggestion
		if (this.contextInternal === undefined) {
```
</issue_to_address>

### Comment 2
<location path="packages/cellix/api-core/src/cellix.ts" line_range="174-183" />
<code_context>
-				await this.tracer.startActiveSpan('cellix.appStart', async (span) => {
-					try {
-						await this.startAllServicesWithTracing();
-						this.serviceInitializedInternal = true;
-						if (!this.contextCreatorInternal) {
-							throw new Error('Context creator missing at appStart');
</code_context>
<issue_to_address>
**issue (bug_risk):** `servicesInitialized` is set to `true` immediately after infrastructure services start, before context creation and application-host creation complete; if either later step throws, `appStart` fails while the returned facade still reports initialized, contradicting the documented appStart-completion state.

**Triggers:** When the context creator or application-services host builder throws after infrastructure startup succeeds.

**Suggested fix:** Set `serviceInitializedInternal` only after the complete appStart sequence succeeds, or document and expose a state that specifically represents infrastructure-service startup rather than appStart completion.

```suggestion
						await this.startAllServicesWithTracing();
						if (!this.contextCreatorInternal) {
							throw new Error('Context creator missing at appStart');
						}
						this.contextInternal = this.contextCreatorInternal(this);
						if (!this.appServicesHostBuilder) {
							throw new Error('Application services factory not provided. Call initializeApplicationServices().');
						}
						this.appServicesHostInternal = this.appServicesHostBuilder(this.contextInternal);
						this.serviceInitializedInternal = true;
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread packages/cellix/api-core/src/cellix.ts Outdated
Comment thread packages/cellix/api-core/src/cellix.ts
Copilot Bot added 2 commits September 18, 2026 17:18
Treat only undefined as a missing context so falsy context values remain
valid, and set servicesInitialized after context and application-host
creation succeed.
Pin patched versions of moment, fast-uri, joi, compression,
proxy-addr, postcss-selector-parser, image-size, ai, and
@ai-sdk/provider-utils so snyk:test reports no vulnerable paths.
@noce-nick

Copy link
Copy Markdown
Member Author

@sourcery-ai review

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've reviewed your changes and they look great!

Fixed security issues:


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant