@primate/angular 0.4.1 → 0.6.0

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,6 +1,4 @@
1
1
  import Runtime from "#Runtime";
2
- import type NextServe from "@primate/core/NextServe";
3
- import type ServeApp from "@primate/core/ServeApp";
4
2
  export default class Default extends Runtime {
5
3
  root: {
6
4
  create: (depth: number, i18n_active: boolean) => string;
@@ -15,6 +13,5 @@ export default class Default extends Runtime {
15
13
  };
16
14
  server: (text: string) => string;
17
15
  };
18
- serve(app: ServeApp, next: NextServe): Promise<ServeApp>;
19
16
  }
20
17
  //# sourceMappingURL=Default.d.ts.map
@@ -1,6 +1,5 @@
1
1
  import create_root from "#create-root";
2
2
  import Runtime from "#Runtime";
3
- import { enableProdMode } from "@angular/core";
4
3
  import * as ts from "typescript";
5
4
  export default class Default extends Runtime {
6
5
  root = {
@@ -51,9 +50,5 @@ export default class Default extends Runtime {
51
50
  return `import "@angular/compiler";\n${result.outputText}`;
52
51
  },
53
52
  };
54
- async serve(app, next) {
55
- app.mode === "production" && enableProdMode();
56
- return super.serve(app, next);
57
- }
58
53
  }
59
54
  //# sourceMappingURL=Default.js.map
@@ -1,5 +1,5 @@
1
1
  import { InjectionToken } from "@angular/core";
2
- import type Dict from "@rcompat/type/Dict";
2
+ import type { Dict } from "@rcompat/type";
3
3
  type RootProps = {
4
4
  views: any[];
5
5
  props: Dict[];
@@ -2,12 +2,14 @@ import "@angular/compiler";
2
2
  import { type Type } from "@angular/core";
3
3
  import FrontendModule from "@primate/core/frontend/Module";
4
4
  import type Render from "@primate/core/frontend/Render";
5
- import "zone.js/node";
5
+ import type NextServe from "@primate/core/NextServe";
6
+ import type ServeApp from "@primate/core/ServeApp";
6
7
  export default class Runtime extends FrontendModule<Type<any>> {
7
8
  name: string;
8
9
  defaultExtensions: string[];
9
10
  layouts: boolean;
10
11
  client: boolean;
11
12
  render: Render<Type<any>>;
13
+ serve(app: ServeApp, next: NextServe): Promise<ServeApp>;
12
14
  }
13
15
  //# sourceMappingURL=Runtime.d.ts.map
@@ -1,11 +1,10 @@
1
1
  import INITIAL_PROPS from "#INITIAL_PROPS";
2
2
  import root from "#root-selector";
3
3
  import "@angular/compiler";
4
- import { importProvidersFrom, provideZoneChangeDetection, } from "@angular/core";
4
+ import { enableProdMode, importProvidersFrom, } from "@angular/core";
5
5
  import { bootstrapApplication, BrowserModule, provideClientHydration, } from "@angular/platform-browser";
6
6
  import { provideServerRendering, renderApplication, } from "@angular/platform-server";
7
7
  import FrontendModule from "@primate/core/frontend/Module";
8
- import "zone.js/node";
9
8
  export default class Runtime extends FrontendModule {
10
9
  name = "angular";
11
10
  defaultExtensions = [".component.ts"];
@@ -16,7 +15,6 @@ export default class Runtime extends FrontendModule {
16
15
  importProvidersFrom(BrowserModule),
17
16
  provideServerRendering(),
18
17
  provideClientHydration(),
19
- provideZoneChangeDetection({ eventCoalescing: true }),
20
18
  {
21
19
  provide: INITIAL_PROPS,
22
20
  useValue: props,
@@ -34,5 +32,9 @@ export default class Runtime extends FrontendModule {
34
32
  head: headMatch?.[1] || "",
35
33
  };
36
34
  };
35
+ async serve(app, next) {
36
+ app.mode === "production" && enableProdMode();
37
+ return super.serve(app, next);
38
+ }
37
39
  }
38
40
  //# sourceMappingURL=Runtime.js.map
@@ -1,6 +1,5 @@
1
- import { type Signal } from "@angular/core";
2
- import type ValidateUpdater from "@primate/core/client/ValidateUpdater";
3
- import type ValidationError from "@primate/core/client/ValidationError";
1
+ import type { Signal } from "@angular/core";
2
+ import type { ValidateUpdater, ValidationError } from "@primate/core/client";
4
3
  type Validated<T> = {
5
4
  error: Signal<null | ValidationError>;
6
5
  loading: Signal<boolean>;
@@ -1,14 +1,15 @@
1
- import "zone.js";
2
1
  import "@angular/compiler";
3
2
  import type ClientData from "@primate/core/client/Data";
4
- import type Dict from "@rcompat/type/Dict";
3
+ import type Mode from "@primate/core/Mode";
4
+ import type { Dict } from "@rcompat/type";
5
5
  type Data = ClientData<{
6
6
  views: string[];
7
7
  props: Dict[];
8
+ mode: Mode;
8
9
  }>;
9
- export default class AngularClient {
10
+ export default class AngularApp {
10
11
  #private;
11
12
  static mount(_view: string, data: ClientData<Data>): Promise<void>;
12
13
  }
13
14
  export {};
14
- //# sourceMappingURL=index.d.ts.map
15
+ //# sourceMappingURL=app.d.ts.map
@@ -1,7 +1,6 @@
1
1
  import INITIAL_PROPS from "#INITIAL_PROPS";
2
- import "zone.js";
3
2
  import "@angular/compiler";
4
- import { NgZone, provideZoneChangeDetection, } from "@angular/core";
3
+ import { enableProdMode, } from "@angular/core";
5
4
  import { bootstrapApplication, provideClientHydration, } from "@angular/platform-browser";
6
5
  import spa from "@primate/core/client/spa";
7
6
  import root from "angular:root";
@@ -15,21 +14,22 @@ const make_props = (data) => ({
15
14
  },
16
15
  update: () => undefined,
17
16
  });
18
- export default class AngularClient {
17
+ export default class AngularApp {
19
18
  static #app;
20
19
  static #root;
21
- static #zone;
22
20
  static async mount(_view, data) {
21
+ if (data.mode === "production")
22
+ enableProdMode();
23
23
  const providers = [];
24
- // Add hydration provider for SSR
25
- if (data.ssr) {
24
+ // add hydration provider for SSR
25
+ if (data.ssr)
26
26
  providers.push(provideClientHydration());
27
- }
28
- // Add zone.js change detection
29
- providers.push(provideZoneChangeDetection({ eventCoalescing: true }));
30
- // Create the root view props
27
+ // in non-SSR mode, the HTML won't contain app-root, inject it
28
+ else
29
+ document.body.appendChild(document.createElement("app-root"));
30
+ // create the root view props
31
31
  const props = make_props(data);
32
- // Bootstrap the application
32
+ // bootstrap the application
33
33
  try {
34
34
  this.#app = await bootstrapApplication(root, {
35
35
  providers: [
@@ -54,19 +54,16 @@ export default class AngularClient {
54
54
  catch (error) {
55
55
  console.error("Failed to bootstrap Angular application:", error);
56
56
  }
57
- this.#zone = this.#root.injector.get(NgZone);
58
57
  }
59
58
  static #spa() {
60
59
  window.addEventListener("DOMContentLoaded", () => {
61
60
  spa((next, update) => {
62
61
  const props = { ...make_props(next), update };
63
- this.#zone.run(() => {
64
- this.#root.instance.p = props;
65
- this.#app.tick();
66
- });
62
+ this.#root.instance.p = props;
63
+ this.#app.tick();
67
64
  update?.();
68
65
  });
69
66
  });
70
67
  }
71
68
  }
72
- //# sourceMappingURL=index.js.map
69
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1,25 @@
1
+ import type Validated from "#client/Validated";
2
+ declare const field: <T>(initial: T) => {
3
+ delete: (url: string, options?: {
4
+ headers?: import("@rcompat/type").Dict<string>;
5
+ map?: ((v: T) => import("@rcompat/type").JSONValue) | undefined;
6
+ path?: import("@rcompat/type").JSONPointer;
7
+ } | undefined) => Validated<T>;
8
+ patch: (url: string, options?: {
9
+ headers?: import("@rcompat/type").Dict<string>;
10
+ map?: ((v: T) => import("@rcompat/type").JSONValue) | undefined;
11
+ path?: import("@rcompat/type").JSONPointer;
12
+ } | undefined) => Validated<T>;
13
+ post: (url: string, options?: {
14
+ headers?: import("@rcompat/type").Dict<string>;
15
+ map?: ((v: T) => import("@rcompat/type").JSONValue) | undefined;
16
+ path?: import("@rcompat/type").JSONPointer;
17
+ } | undefined) => Validated<T>;
18
+ put: (url: string, options?: {
19
+ headers?: import("@rcompat/type").Dict<string>;
20
+ map?: ((v: T) => import("@rcompat/type").JSONValue) | undefined;
21
+ path?: import("@rcompat/type").JSONPointer;
22
+ } | undefined) => Validated<T>;
23
+ };
24
+ export default field;
25
+ //# sourceMappingURL=field.d.ts.map
@@ -1,6 +1,5 @@
1
1
  import { signal } from "@angular/core";
2
- import toValidated from "@primate/core/client/toValidated";
3
- import validate from "@primate/core/client/validate";
2
+ import client from "@primate/core/client";
4
3
  function useValidate(init) {
5
4
  const value = signal(init.initial);
6
5
  const loading = signal(false);
@@ -12,7 +11,7 @@ function useValidate(init) {
12
11
  loading.set(true);
13
12
  error.set(null);
14
13
  try {
15
- await validate(init, next);
14
+ await client.validateField(init, next);
16
15
  }
17
16
  catch (e) {
18
17
  // rollback
@@ -25,5 +24,6 @@ function useValidate(init) {
25
24
  }
26
25
  return { error, loading, update, value };
27
26
  }
28
- export default toValidated(useValidate);
29
- //# sourceMappingURL=validate.js.map
27
+ const field = client.toValidated(useValidate);
28
+ export default field;
29
+ //# sourceMappingURL=field.js.map
@@ -0,0 +1,23 @@
1
+ import { type Signal } from "@angular/core";
2
+ import { type FormInit } from "@primate/core/client";
3
+ import type { Dict } from "@rcompat/type";
4
+ type Field<T> = {
5
+ name: string;
6
+ value: T;
7
+ errors: Signal<readonly string[]>;
8
+ error: Signal<string | null>;
9
+ };
10
+ type FormView<Values extends Dict> = {
11
+ id: string;
12
+ submitting: Signal<boolean>;
13
+ submit: (event?: Event) => Promise<void>;
14
+ errors: Signal<readonly string[]>;
15
+ field: <K extends keyof Values & string>(name: K) => Field<Values[K]>;
16
+ };
17
+ type Initial<Values extends Dict> = FormInit & {
18
+ initial?: Values;
19
+ };
20
+ declare function form<Values extends Dict>(init: Initial<Values>): FormView<Values>;
21
+ declare function form(init?: FormInit): FormView<Dict>;
22
+ export default form;
23
+ //# sourceMappingURL=form.d.ts.map
@@ -0,0 +1,47 @@
1
+ import { DestroyRef, computed, inject, signal, } from "@angular/core";
2
+ import core from "@primate/core/client";
3
+ function try_destroy_ref() {
4
+ try {
5
+ return inject(DestroyRef);
6
+ }
7
+ catch {
8
+ return null;
9
+ }
10
+ }
11
+ function form(init) {
12
+ const { initial, ...form_init } = init ?? {};
13
+ const controller = core.createForm(form_init);
14
+ const values = initial ?? {};
15
+ const snap = signal(controller.read());
16
+ const unsub = controller.subscribe((next) => snap.set(next));
17
+ try_destroy_ref()?.onDestroy(unsub);
18
+ const submitting = computed(() => snap().submitting);
19
+ const errors = computed(() => snap().errors.form);
20
+ // cache per-field views so templates calling field("x") repeatedly
21
+ // don't recreate computed signals every change detection cycle
22
+ const cache = new Map();
23
+ function field(name) {
24
+ const key = name;
25
+ const cached = cache.get(key);
26
+ if (cached)
27
+ return cached;
28
+ const form_errors = computed(() => snap().errors.fields[key] ?? []);
29
+ const view = {
30
+ name: key,
31
+ value: values[name],
32
+ errors: form_errors,
33
+ error: computed(() => form_errors()[0] ?? null),
34
+ };
35
+ cache.set(key, view);
36
+ return view;
37
+ }
38
+ return {
39
+ id: controller.id,
40
+ submitting,
41
+ submit: (event) => controller.submit(event),
42
+ errors,
43
+ field,
44
+ };
45
+ }
46
+ export default form;
47
+ //# sourceMappingURL=form.js.map
@@ -72,7 +72,7 @@ export default class RootComponent implements OnDestroy {
72
72
  @Input({ required: true })
73
73
  set p(value: RootProps) {
74
74
  this.#p = value;
75
- this.#cdr.markForCheck(); // root on default CD, zone ticks traverse
75
+ this.#cdr.markForCheck(); // root on default CD
76
76
  }
77
77
  get p(): RootProps { return this.#p; }
78
78
  get P(): RootProps { return this.#p; }
@@ -94,9 +94,9 @@ export default class RootComponent implements OnDestroy {
94
94
  return { ...base, slot };
95
95
  }
96
96
 
97
- ngAfterViewInit() {
97
+ ${i18n_active ? `ngAfterViewInit() {
98
98
  t[sInternal].restore();
99
- }
99
+ }` : ""}
100
100
 
101
101
  ngOnDestroy() { this.#off?.(); }
102
102
  }
@@ -1,2 +1,2 @@
1
- export { default } from "#client/index";
1
+ export { default } from "#client/app";
2
2
  //# sourceMappingURL=browser.d.ts.map
@@ -1,2 +1,2 @@
1
- export { default } from "#client/index";
1
+ export { default } from "#client/app";
2
2
  //# sourceMappingURL=browser.js.map
@@ -0,0 +1,30 @@
1
+ import type Validated from "#client/Validated";
2
+ import form from "#client/form";
3
+ declare const client: {
4
+ form: typeof form;
5
+ field: <T>(initial: T) => {
6
+ delete: (url: string, options?: {
7
+ headers?: import("@rcompat/type").Dict<string>;
8
+ map?: ((v: T) => import("@rcompat/type").JSONValue) | undefined;
9
+ path?: import("@rcompat/type").JSONPointer;
10
+ } | undefined) => Validated<T>;
11
+ patch: (url: string, options?: {
12
+ headers?: import("@rcompat/type").Dict<string>;
13
+ map?: ((v: T) => import("@rcompat/type").JSONValue) | undefined;
14
+ path?: import("@rcompat/type").JSONPointer;
15
+ } | undefined) => Validated<T>;
16
+ post: (url: string, options?: {
17
+ headers?: import("@rcompat/type").Dict<string>;
18
+ map?: ((v: T) => import("@rcompat/type").JSONValue) | undefined;
19
+ path?: import("@rcompat/type").JSONPointer;
20
+ } | undefined) => Validated<T>;
21
+ put: (url: string, options?: {
22
+ headers?: import("@rcompat/type").Dict<string>;
23
+ map?: ((v: T) => import("@rcompat/type").JSONValue) | undefined;
24
+ path?: import("@rcompat/type").JSONPointer;
25
+ } | undefined) => Validated<T>;
26
+ };
27
+ };
28
+ export default client;
29
+ export type { Validated };
30
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1,5 @@
1
+ import field from "#client/field";
2
+ import form from "#client/form";
3
+ const client = { form, field };
4
+ export default client;
5
+ //# sourceMappingURL=client.js.map
package/package.json CHANGED
@@ -1,40 +1,37 @@
1
1
  {
2
2
  "name": "@primate/angular",
3
- "version": "0.4.1",
4
- "description": "Primate Angular frontend",
3
+ "version": "0.6.0",
4
+ "description": "Angular for Primate",
5
5
  "homepage": "https://primate.run/docs/frontend/angular",
6
6
  "bugs": "https://github.com/primate-run/primate/issues",
7
+ "type": "module",
7
8
  "license": "MIT",
8
- "files": [
9
- "/lib/**/*.js",
10
- "/lib/**/*.d.ts",
11
- "!/**/*.spec.*"
12
- ],
13
9
  "repository": {
14
10
  "type": "git",
15
11
  "url": "https://github.com/primate-run/primate",
16
12
  "directory": "packages/angular"
17
13
  },
14
+ "files": [
15
+ "/lib/**/*.js",
16
+ "/lib/**/*.d.ts",
17
+ "!/**/*.spec.*"
18
+ ],
18
19
  "dependencies": {
19
- "@angular/common": "^20.3.2",
20
- "@angular/compiler": "^20.3.2",
21
- "@angular/core": "^20.3.2",
22
- "@angular/platform-browser": "^20.3.2",
23
- "@angular/platform-server": "^20.3.2",
24
- "@angular/ssr": "^20.3.3",
25
- "@rcompat/assert": "^0.3.1",
26
- "@rcompat/build": "^0.14.0",
27
- "@rcompat/crypto": "^0.10.0",
28
- "@rcompat/fs": "^0.21.1",
29
- "@rcompat/record": "^0.9.1",
30
- "typescript": "^5.9.2",
31
- "zone.js": "^0.15.1",
32
- "@primate/core": "^0.3.1"
20
+ "@angular/common": "^21.1.4",
21
+ "@angular/compiler": "^21.1.4",
22
+ "@angular/core": "^21.1.4",
23
+ "@angular/platform-browser": "^21.1.4",
24
+ "@angular/platform-server": "^21.1.4",
25
+ "@angular/ssr": "^21.1.4",
26
+ "typescript": "^5.9.3",
27
+ "@primate/core": "^0.5.0"
28
+ },
29
+ "devDependencies": {
30
+ "@rcompat/type": "^0.9.0"
33
31
  },
34
32
  "peerDependencies": {
35
- "primate": "^0.34.0"
33
+ "primate": "^0.36.0"
36
34
  },
37
- "type": "module",
38
35
  "imports": {
39
36
  "#*": {
40
37
  "apekit": "./src/private/*.ts",
@@ -1,25 +0,0 @@
1
- import type Validated from "#client/Validated";
2
- declare const _default: <T>(initial: T) => {
3
- delete: (url: string, options?: {
4
- headers?: import("@rcompat/type/Dict").default<string>;
5
- map?: ((v: T) => import("@rcompat/type/JSONValue").default) | undefined;
6
- path?: import("@rcompat/type/JSONPointer").default;
7
- } | undefined) => Validated<T>;
8
- patch: (url: string, options?: {
9
- headers?: import("@rcompat/type/Dict").default<string>;
10
- map?: ((v: T) => import("@rcompat/type/JSONValue").default) | undefined;
11
- path?: import("@rcompat/type/JSONPointer").default;
12
- } | undefined) => Validated<T>;
13
- post: (url: string, options?: {
14
- headers?: import("@rcompat/type/Dict").default<string>;
15
- map?: ((v: T) => import("@rcompat/type/JSONValue").default) | undefined;
16
- path?: import("@rcompat/type/JSONPointer").default;
17
- } | undefined) => Validated<T>;
18
- put: (url: string, options?: {
19
- headers?: import("@rcompat/type/Dict").default<string>;
20
- map?: ((v: T) => import("@rcompat/type/JSONValue").default) | undefined;
21
- path?: import("@rcompat/type/JSONPointer").default;
22
- } | undefined) => Validated<T>;
23
- };
24
- export default _default;
25
- //# sourceMappingURL=validate.d.ts.map
@@ -1,2 +0,0 @@
1
- export { default } from "#client/Validated";
2
- //# sourceMappingURL=Validated.d.ts.map
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=Validated.js.map
@@ -1,2 +0,0 @@
1
- export { default } from "#client/validate";
2
- //# sourceMappingURL=validate.d.ts.map
@@ -1,2 +0,0 @@
1
- export { default } from "#client/validate";
2
- //# sourceMappingURL=validate.js.map