crelte 0.5.10 → 0.5.12

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.
Files changed (133) hide show
  1. package/dist/bodyClass/BodyClass.d.ts +39 -0
  2. package/dist/bodyClass/BodyClass.d.ts.map +1 -0
  3. package/dist/bodyClass/BodyClass.js +51 -0
  4. package/dist/bodyClass/ClientBodyClass.d.ts +12 -0
  5. package/dist/bodyClass/ClientBodyClass.d.ts.map +1 -0
  6. package/dist/bodyClass/ClientBodyClass.js +57 -0
  7. package/dist/bodyClass/ServerBodyClass.d.ts +12 -0
  8. package/dist/bodyClass/ServerBodyClass.d.ts.map +1 -0
  9. package/dist/bodyClass/ServerBodyClass.js +47 -0
  10. package/dist/bodyClass/index.d.ts +2 -0
  11. package/dist/bodyClass/index.d.ts.map +1 -0
  12. package/dist/bodyClass/index.js +1 -0
  13. package/dist/cookies/ClientCookies.d.ts +8 -3
  14. package/dist/cookies/ClientCookies.d.ts.map +1 -1
  15. package/dist/cookies/ClientCookies.js +31 -7
  16. package/dist/cookies/Cookies.d.ts +42 -0
  17. package/dist/cookies/Cookies.d.ts.map +1 -0
  18. package/dist/cookies/Cookies.js +44 -0
  19. package/dist/cookies/ServerCookies.d.ts +3 -2
  20. package/dist/cookies/ServerCookies.d.ts.map +1 -1
  21. package/dist/cookies/ServerCookies.js +6 -4
  22. package/dist/cookies/index.d.ts +1 -25
  23. package/dist/cookies/index.d.ts.map +1 -1
  24. package/dist/cookies/index.js +1 -1
  25. package/dist/crelte.d.ts +7 -1
  26. package/dist/crelte.d.ts.map +1 -1
  27. package/dist/crelte.js +2 -1
  28. package/dist/index.d.ts +13 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +9 -0
  31. package/dist/init/client.d.ts +1 -8
  32. package/dist/init/client.d.ts.map +1 -1
  33. package/dist/init/client.js +26 -24
  34. package/dist/init/server.d.ts.map +1 -1
  35. package/dist/init/server.js +12 -4
  36. package/dist/init/shared.d.ts +1 -0
  37. package/dist/init/shared.d.ts.map +1 -1
  38. package/dist/init/shared.js +16 -5
  39. package/dist/loadData/Globals.d.ts.map +1 -1
  40. package/dist/node/index.js +1 -1
  41. package/dist/plugins/Events.d.ts +12 -7
  42. package/dist/plugins/Events.d.ts.map +1 -1
  43. package/dist/plugins/Plugins.d.ts +36 -1
  44. package/dist/plugins/Plugins.d.ts.map +1 -1
  45. package/dist/plugins/Plugins.js +32 -0
  46. package/dist/queries/Queries.d.ts +30 -5
  47. package/dist/queries/Queries.d.ts.map +1 -1
  48. package/dist/queries/Queries.js +19 -2
  49. package/dist/queries/gql.d.ts +2 -2
  50. package/dist/queries/gql.d.ts.map +1 -1
  51. package/dist/queries/index.d.ts +47 -2
  52. package/dist/queries/index.d.ts.map +1 -1
  53. package/dist/queries/index.js +2 -2
  54. package/dist/queries/vars.d.ts +2 -0
  55. package/dist/queries/vars.d.ts.map +1 -1
  56. package/dist/queries/vars.js +10 -0
  57. package/dist/routing/route/Request.d.ts +1 -1
  58. package/dist/routing/route/Request.d.ts.map +1 -1
  59. package/dist/routing/route/Request.js +7 -3
  60. package/dist/routing/router/BaseRouter.d.ts +2 -1
  61. package/dist/routing/router/BaseRouter.d.ts.map +1 -1
  62. package/dist/routing/router/BaseRouter.js +4 -2
  63. package/dist/routing/router/ClientRouter.d.ts.map +1 -1
  64. package/dist/routing/router/ClientRouter.js +21 -15
  65. package/dist/routing/router/Router.d.ts +2 -1
  66. package/dist/routing/router/Router.d.ts.map +1 -1
  67. package/dist/routing/router/Router.js +10 -13
  68. package/dist/routing/utils.d.ts +1 -0
  69. package/dist/routing/utils.d.ts.map +1 -1
  70. package/dist/routing/utils.js +1 -1
  71. package/dist/server/CrelteServer.d.ts +1 -0
  72. package/dist/server/CrelteServer.d.ts.map +1 -1
  73. package/dist/server/CrelteServer.js +5 -2
  74. package/dist/server/ServerRouter.d.ts.map +1 -1
  75. package/dist/server/ServerRouter.js +17 -7
  76. package/dist/server/queries/QueryGqlRoute.d.ts +28 -0
  77. package/dist/server/queries/QueryGqlRoute.d.ts.map +1 -0
  78. package/dist/server/queries/QueryGqlRoute.js +194 -0
  79. package/dist/server/queries/QueryHandleRoute.d.ts +12 -0
  80. package/dist/server/queries/QueryHandleRoute.d.ts.map +1 -0
  81. package/dist/server/queries/QueryHandleRoute.js +24 -0
  82. package/dist/server/queries/queries.d.ts.map +1 -1
  83. package/dist/server/queries/queries.js +42 -19
  84. package/dist/server/queries/routes.d.ts +7 -30
  85. package/dist/server/queries/routes.d.ts.map +1 -1
  86. package/dist/server/queries/routes.js +13 -199
  87. package/dist/std/stores/StagedWritable.d.ts +48 -0
  88. package/dist/std/stores/StagedWritable.d.ts.map +1 -0
  89. package/dist/std/stores/StagedWritable.js +84 -0
  90. package/dist/std/stores/index.d.ts +2 -1
  91. package/dist/std/stores/index.d.ts.map +1 -1
  92. package/dist/std/stores/index.js +2 -1
  93. package/dist/std/sync/Barrier.js +1 -1
  94. package/dist/utils.d.ts +9 -0
  95. package/dist/utils.d.ts.map +1 -1
  96. package/dist/utils.js +11 -0
  97. package/package.json +5 -1
  98. package/src/bodyClass/BodyClass.ts +72 -0
  99. package/src/bodyClass/ClientBodyClass.ts +62 -0
  100. package/src/bodyClass/ServerBodyClass.ts +65 -0
  101. package/src/bodyClass/index.ts +1 -0
  102. package/src/cookies/ClientCookies.ts +41 -10
  103. package/src/cookies/Cookies.ts +70 -0
  104. package/src/cookies/ServerCookies.ts +9 -6
  105. package/src/cookies/index.ts +5 -29
  106. package/src/crelte.ts +9 -0
  107. package/src/index.ts +15 -1
  108. package/src/init/client.ts +29 -24
  109. package/src/init/server.ts +12 -4
  110. package/src/init/shared.ts +18 -6
  111. package/src/loadData/Globals.ts +1 -1
  112. package/src/node/index.ts +1 -1
  113. package/src/plugins/Events.ts +22 -7
  114. package/src/plugins/Plugins.ts +66 -1
  115. package/src/queries/Queries.ts +47 -14
  116. package/src/queries/gql.ts +2 -2
  117. package/src/queries/index.ts +71 -0
  118. package/src/queries/vars.ts +13 -0
  119. package/src/routing/route/Request.ts +11 -4
  120. package/src/routing/router/BaseRouter.ts +4 -2
  121. package/src/routing/router/ClientRouter.ts +26 -18
  122. package/src/routing/router/Router.ts +10 -11
  123. package/src/routing/utils.ts +1 -1
  124. package/src/server/CrelteServer.ts +4 -2
  125. package/src/server/ServerRouter.ts +18 -7
  126. package/src/server/queries/QueryGqlRoute.ts +224 -0
  127. package/src/server/queries/QueryHandleRoute.ts +37 -0
  128. package/src/server/queries/queries.ts +57 -21
  129. package/src/server/queries/routes.ts +25 -229
  130. package/src/std/stores/StagedWritable.ts +96 -0
  131. package/src/std/stores/index.ts +2 -1
  132. package/src/std/sync/Barrier.ts +1 -1
  133. package/src/utils.ts +15 -0
@@ -0,0 +1,84 @@
1
+ import Writable from './Writable.js';
2
+ export default class StagedWritable {
3
+ inner;
4
+ // -1 = not staged, 0 = staged but no new value, 1 = staged
5
+ mode;
6
+ staged;
7
+ /**
8
+ * Creates a new StagedWritable
9
+ *
10
+ * @param def A default value
11
+ */
12
+ constructor(def) {
13
+ this.inner = new Writable(def);
14
+ this.mode = -1;
15
+ this.staged = undefined;
16
+ }
17
+ /**
18
+ * Returns true if the store is currently staged
19
+ */
20
+ isStaged() {
21
+ return this.mode >= 0;
22
+ }
23
+ /**
24
+ * Returns a new StagedWritable which is staged and has the same value as the current one
25
+ * To commit the staged value, call `commit` on the returned StagedWritable
26
+ */
27
+ stage() {
28
+ const n = Object.create(this);
29
+ n.inner = this.inner;
30
+ n.mode = 0;
31
+ n.staged = undefined;
32
+ return n;
33
+ }
34
+ /**
35
+ * The function get's called once with the current value and then when the
36
+ * values changes
37
+ *
38
+ * #### Note
39
+ * This does not check for equality like svelte.
40
+ *
41
+ * @return a function which should be called to unsubscribe
42
+ */
43
+ subscribe(fn, invalidate) {
44
+ return this.inner.subscribe(fn, invalidate);
45
+ }
46
+ /**
47
+ * Either updates the store and calls all subscribers with the value or
48
+ * updates the stateful value if previously toStateful was called
49
+ */
50
+ set(inner) {
51
+ if (this.mode >= 0) {
52
+ this.mode = 1;
53
+ this.staged = inner;
54
+ }
55
+ else {
56
+ this.inner.set(inner);
57
+ }
58
+ }
59
+ /**
60
+ * If the value was staged, then update the store and call all subscribers with the value
61
+ */
62
+ commit() {
63
+ if (this.mode < 0)
64
+ throw new Error('not staged, call stage first');
65
+ if (this.mode === 1)
66
+ this.inner.set(this.staged);
67
+ this.staged = undefined;
68
+ this.mode = -1;
69
+ }
70
+ /**
71
+ * Get the current value either staged or not
72
+ */
73
+ get() {
74
+ if (this.mode === 1)
75
+ return this.staged;
76
+ return this.inner.get();
77
+ }
78
+ readonly() {
79
+ return this.inner.readonly();
80
+ }
81
+ readclone() {
82
+ return this.inner.readclone();
83
+ }
84
+ }
@@ -1,5 +1,6 @@
1
1
  import Readable from './Readable.js';
2
2
  import Writable from './Writable.js';
3
3
  import Readclone from './Readclone.js';
4
- export { Writable, Readable, Readclone };
4
+ import StagedWritable from './StagedWritable.js';
5
+ export { Writable, Readable, Readclone, StagedWritable };
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/std/stores/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,eAAe,CAAC;AACrC,OAAO,QAAQ,MAAM,eAAe,CAAC;AACrC,OAAO,SAAS,MAAM,gBAAgB,CAAC;AAEvC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/std/stores/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,eAAe,CAAC;AACrC,OAAO,QAAQ,MAAM,eAAe,CAAC;AACrC,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,cAAc,MAAM,qBAAqB,CAAC;AAEjD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import Readable from './Readable.js';
2
2
  import Writable from './Writable.js';
3
3
  import Readclone from './Readclone.js';
4
- export { Writable, Readable, Readclone };
4
+ import StagedWritable from './StagedWritable.js';
5
+ export { Writable, Readable, Readclone, StagedWritable };
@@ -61,7 +61,7 @@ export default class Barrier {
61
61
  }
62
62
  _maybeTrigger() {
63
63
  const ready = this.listeners.every(v => v === null || v.ready);
64
- // if all are ready
64
+ // if not all are ready
65
65
  if (!ready)
66
66
  return false;
67
67
  // send the last value to all of them
package/dist/utils.d.ts CHANGED
@@ -1,4 +1,13 @@
1
1
  export declare function objClone(obj: any): any;
2
2
  export declare function isPromise<T>(p: Promise<T> | T): p is Promise<T>;
3
+ /**
4
+ * A helper to chain promises without needing a microtask if its not a promise
5
+ * Equivalent to:
6
+ * ```
7
+ * const val = await p;
8
+ * then(val);
9
+ * ```
10
+ */
11
+ export declare function promiseThen<T, R = void>(p: Promise<T> | T, then: (val: T) => R): Promise<R> | R;
3
12
  export declare function urlWithPath(url: string, path?: string): URL;
4
13
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAGA,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,CAMtC;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAE/D;AAGD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,GAAG,CAI3D"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAGA,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,CAMtC;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAE/D;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,EACtC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EACjB,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GACjB,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAEhB;AAGD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,GAAG,CAI3D"}
package/dist/utils.js CHANGED
@@ -9,6 +9,17 @@ export function objClone(obj) {
9
9
  export function isPromise(p) {
10
10
  return typeof p?.then === 'function';
11
11
  }
12
+ /**
13
+ * A helper to chain promises without needing a microtask if its not a promise
14
+ * Equivalent to:
15
+ * ```
16
+ * const val = await p;
17
+ * then(val);
18
+ * ```
19
+ */
20
+ export function promiseThen(p, then) {
21
+ return isPromise(p) ? p.then(then) : then(p);
22
+ }
12
23
  // the pathname is always replaced
13
24
  export function urlWithPath(url, path) {
14
25
  const u = new URL(url);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crelte",
3
- "version": "0.5.10",
3
+ "version": "0.5.12",
4
4
  "author": "Crelte <support@crelte.com>",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -72,6 +72,10 @@
72
72
  "types": "./dist/cookies/index.d.ts",
73
73
  "default": "./dist/cookies/index.js"
74
74
  },
75
+ "./bodyClass": {
76
+ "types": "./dist/bodyClass/index.d.ts",
77
+ "default": "./dist/bodyClass/index.js"
78
+ },
75
79
  "./blocks": {
76
80
  "types": "./dist/blocks/index.d.ts",
77
81
  "default": "./dist/blocks/index.js"
@@ -0,0 +1,72 @@
1
+ import StagedWritable from '../std/stores/StagedWritable.js';
2
+
3
+ export default class BodyClass {
4
+ private inner: PlatformBodyClass;
5
+ private store: StagedWritable<void>;
6
+
7
+ constructor(inner: PlatformBodyClass, store?: StagedWritable<void>) {
8
+ this.inner = inner;
9
+ this.store = store ?? new StagedWritable(void 0);
10
+ }
11
+
12
+ subscribe(
13
+ fn: (val: BodyClass) => void,
14
+ invalidate?: () => void,
15
+ ): () => void {
16
+ return this.store.subscribe(() => fn(this), invalidate);
17
+ }
18
+
19
+ /**
20
+ * Checks if the body contains the given class
21
+ */
22
+ contains(cls: string): boolean {
23
+ return this.inner.contains(cls);
24
+ }
25
+
26
+ /**
27
+ * Adds the given classes to the body
28
+ */
29
+ add(...classes: string[]): void {
30
+ this.inner.add(...classes);
31
+ this.store.set();
32
+ }
33
+
34
+ /**
35
+ * Toggles the given class on the body
36
+ *
37
+ * ## Warning
38
+ * toggle without `force` should almost never be used
39
+ * on the server. If you call this for example in loadData
40
+ * the server will add the class and the client will the remove
41
+ * it.
42
+ */
43
+ toggle(cls: string, force?: boolean): void {
44
+ this.inner.toggle(cls, force);
45
+ this.store.set();
46
+ }
47
+
48
+ remove(...classes: string[]): void {
49
+ this.inner.remove(...classes);
50
+ this.store.set();
51
+ }
52
+
53
+ /** @hidden */
54
+ z_toRequest(): BodyClass {
55
+ return new BodyClass(this.inner.toRequest(), this.store.stage());
56
+ }
57
+
58
+ /** @hidden */
59
+ z_render(): void {
60
+ this.inner.render?.();
61
+ this.store.commit();
62
+ }
63
+ }
64
+
65
+ export interface PlatformBodyClass {
66
+ contains(cls: string): boolean;
67
+ add(...classes: string[]): void;
68
+ toggle(cls: string, force?: boolean): void;
69
+ remove(...classes: string[]): void;
70
+ toRequest(): PlatformBodyClass;
71
+ render?: () => void;
72
+ }
@@ -0,0 +1,62 @@
1
+ import { PlatformBodyClass } from './BodyClass.js';
2
+
3
+ export default class ClientBodyClass implements PlatformBodyClass {
4
+ private inner: Set<string> | null;
5
+
6
+ constructor(inner?: Set<string>) {
7
+ this.inner = inner ?? null;
8
+ }
9
+
10
+ contains(cls: string): boolean {
11
+ if (this.inner) return this.inner.has(cls);
12
+ return cl().contains(cls);
13
+ }
14
+
15
+ add(...classes: string[]): void {
16
+ if (this.inner) classes.forEach(cls => this.inner!.add(cls));
17
+ else cl().add(...classes);
18
+ }
19
+
20
+ toggle(cls: string, force?: boolean): void {
21
+ if (this.inner) {
22
+ const add =
23
+ typeof force === 'boolean' ? force : !this.inner.has(cls);
24
+
25
+ if (add) this.inner.add(cls);
26
+ else this.inner.delete(cls);
27
+ } else {
28
+ cl().toggle(cls, force);
29
+ }
30
+ }
31
+
32
+ remove(...classes: string[]): void {
33
+ if (this.inner) classes.forEach(cls => this.inner!.delete(cls));
34
+ else cl().remove(...classes);
35
+ }
36
+
37
+ toRequest(): ClientBodyClass {
38
+ const inner = new Set(cl());
39
+ return new ClientBodyClass(inner);
40
+ }
41
+
42
+ render(): void {
43
+ if (!this.inner) throw new Error('call toRequest first');
44
+ const current = new Set(cl());
45
+
46
+ for (const cls of this.inner) {
47
+ const existed = current.delete(cls);
48
+ if (!existed) cl().add(cls);
49
+ }
50
+
51
+ // now lets remove all classes that still exist in current
52
+ for (const cls of current) {
53
+ cl().remove(cls);
54
+ }
55
+
56
+ this.inner = null;
57
+ }
58
+ }
59
+
60
+ function cl(): DOMTokenList {
61
+ return document.body.classList;
62
+ }
@@ -0,0 +1,65 @@
1
+ import { PlatformBodyClass } from './BodyClass.js';
2
+
3
+ export default class ServerBodyClass implements PlatformBodyClass {
4
+ private inner: Set<string>;
5
+
6
+ constructor() {
7
+ this.inner = new Set();
8
+ }
9
+
10
+ contains(cls: string): boolean {
11
+ return this.inner.has(cls);
12
+ }
13
+
14
+ add(...classes: string[]): void {
15
+ validate(classes);
16
+ classes.forEach(cls => this.inner.add(cls));
17
+ }
18
+
19
+ toggle(cls: string, force?: boolean): void {
20
+ validate([cls]);
21
+
22
+ if (import.meta.env.DEV && typeof force !== 'boolean') {
23
+ console.warn(
24
+ 'Using toggle without force on the server can lead ' +
25
+ 'to unexpected results. Because in a loadData the server will add it ' +
26
+ 'and the client will afterwards remove it.',
27
+ );
28
+ }
29
+
30
+ const add = typeof force === 'boolean' ? force : !this.inner.has(cls);
31
+
32
+ if (add) this.inner.add(cls);
33
+ else this.inner.delete(cls);
34
+ }
35
+
36
+ remove(...classes: string[]): void {
37
+ validate(classes);
38
+ classes.forEach(cls => this.inner.delete(cls));
39
+ }
40
+
41
+ toRequest(): ServerBodyClass {
42
+ return this;
43
+ }
44
+
45
+ z_processHtmlTemplate(html: string): string {
46
+ const SEARCH_STR = '<!--body-class-->';
47
+ if (this.inner.size && !html.includes(SEARCH_STR)) {
48
+ throw new Error(
49
+ 'index.html needs to contain `class="<!--body-class-->"`',
50
+ );
51
+ }
52
+
53
+ return html.replace(SEARCH_STR, Array.from(this.inner).join(' '));
54
+ }
55
+ }
56
+
57
+ function validate(classes: string[]) {
58
+ for (const cls of classes) {
59
+ if (cls.includes(' ')) {
60
+ throw new Error(
61
+ `Invalid class name "${cls}". Class names must not contain spaces.`,
62
+ );
63
+ }
64
+ }
65
+ }
@@ -0,0 +1 @@
1
+ export { default as BodyClass } from './BodyClass.js';
@@ -1,26 +1,57 @@
1
- import { Cookies, RemoveOptions, SetOptions } from './index.js';
2
- import { parseCookies, setCookieToString } from './utils.js';
1
+ import { PlatformCookies, RemoveOptions, SetOptions } from './Cookies.js';
2
+ import { parseCookies, SetCookie, setCookieToString } from './utils.js';
3
3
 
4
- // the philosophy here is that document.cookie is the source of truth
5
- // so we don't cache cookies here
6
- // if this changes, modify init/client.ts
7
- export default class ClientCookies implements Cookies {
8
- constructor() {}
4
+ export default class ClientCookies implements PlatformCookies {
5
+ private inner: Map<string, string> | null;
6
+ private setCookies: Map<string, SetCookie> | null;
7
+
8
+ constructor(
9
+ inner?: Map<string, string>,
10
+ setCookies?: Map<string, SetCookie>,
11
+ ) {
12
+ this.inner = inner ?? null;
13
+ this.setCookies = setCookies ?? null;
14
+ }
9
15
 
10
16
  get(name: string): string | null {
11
- const cookies = getCookies();
12
- return cookies.get(name) ?? null;
17
+ if (!this.inner || !this.setCookies) {
18
+ const cookies = getCookies();
19
+ return cookies.get(name) ?? null;
20
+ }
21
+
22
+ const setCookie = this.setCookies.get(name);
23
+ if (setCookie) {
24
+ return (setCookie.maxAge ?? 1) > 0 ? setCookie.value : null;
25
+ }
26
+
27
+ return this.inner.get(name) ?? null;
13
28
  }
14
29
 
15
30
  set(name: string, value: string, opts?: SetOptions): void {
16
31
  const setCookie = { name, value, path: '/', ...opts };
17
32
 
18
- document.cookie = setCookieToString(setCookie);
33
+ if (this.setCookies) this.setCookies.set(name, setCookie);
34
+ else document.cookie = setCookieToString(setCookie);
19
35
  }
20
36
 
21
37
  remove(name: string, opts?: RemoveOptions): void {
22
38
  this.set(name, '', { ...opts, maxAge: 0 });
23
39
  }
40
+
41
+ toRequest(): ClientCookies {
42
+ return new ClientCookies(getCookies(), new Map());
43
+ }
44
+
45
+ render(): void {
46
+ if (!this.setCookies) throw new Error('call toRequest first');
47
+
48
+ for (const setCookie of this.setCookies.values()) {
49
+ document.cookie = setCookieToString(setCookie);
50
+ }
51
+
52
+ this.inner = null;
53
+ this.setCookies = null;
54
+ }
24
55
  }
25
56
 
26
57
  function getCookies(): Map<string, string> {
@@ -0,0 +1,70 @@
1
+ import StagedWritable from '../std/stores/StagedWritable.js';
2
+
3
+ export type SetOptions = {
4
+ maxAge?: number;
5
+ path?: string;
6
+ domain?: string;
7
+ secure?: boolean;
8
+ httpOnly?: boolean;
9
+ };
10
+
11
+ export type RemoveOptions = Omit<SetOptions, 'maxAge'>;
12
+
13
+ export default class Cookies {
14
+ private inner: PlatformCookies;
15
+ private store: StagedWritable<void>;
16
+
17
+ constructor(inner: PlatformCookies, store?: StagedWritable<void>) {
18
+ this.inner = inner;
19
+ this.store = store ?? new StagedWritable(void 0);
20
+ }
21
+
22
+ subscribe(fn: (val: Cookies) => void, invalidate?: () => void): () => void {
23
+ return this.store.subscribe(() => fn(this), invalidate);
24
+ }
25
+
26
+ /**
27
+ * returns the value of the cookie
28
+ */
29
+ get(name: string): string | null {
30
+ return this.inner.get(name);
31
+ }
32
+
33
+ /**
34
+ * sets the value of the cookie
35
+ *
36
+ * #### Note
37
+ * path defaults to '/'
38
+ */
39
+ set(name: string, value: string, opts?: SetOptions): void {
40
+ this.inner.set(name, value, opts);
41
+ this.store.set();
42
+ }
43
+
44
+ /**
45
+ * removes the cookie
46
+ */
47
+ remove(name: string, opts?: RemoveOptions): void {
48
+ this.inner.remove(name, opts);
49
+ this.store.set();
50
+ }
51
+
52
+ /** @hidden */
53
+ z_toRequest(): Cookies {
54
+ return new Cookies(this.inner.toRequest(), this.store.stage());
55
+ }
56
+
57
+ /** @hidden */
58
+ z_render(): void {
59
+ this.inner.render?.();
60
+ this.store.commit();
61
+ }
62
+ }
63
+
64
+ export interface PlatformCookies {
65
+ get(name: string): string | null;
66
+ set(name: string, value: string, opts?: SetOptions): void;
67
+ remove(name: string, opts?: RemoveOptions): void;
68
+ toRequest(): PlatformCookies;
69
+ render?: () => void;
70
+ }
@@ -1,11 +1,11 @@
1
- import { Cookies, RemoveOptions, SetOptions } from './index.js';
1
+ import { PlatformCookies, RemoveOptions, SetOptions } from './Cookies.js';
2
2
  import { parseCookies, type SetCookie, setCookieToString } from './utils.js';
3
3
 
4
4
  /**
5
5
  * #### Warning
6
6
  * This is not stable and should only be used internally by crelte
7
7
  */
8
- export default class ServerCookies implements Cookies {
8
+ export default class ServerCookies implements PlatformCookies {
9
9
  requestCookies: Map<string, string>;
10
10
  setCookies: Map<string, SetCookie>;
11
11
 
@@ -14,12 +14,11 @@ export default class ServerCookies implements Cookies {
14
14
  this.setCookies = new Map();
15
15
  }
16
16
 
17
- /// Rethrns the value of the cookie with the given name, or null if it doesn't exist.
17
+ /// Returns the value of the cookie with the given name, or null if it doesn't exist.
18
18
  get(name: string): string | null {
19
19
  const setCookie = this.setCookies.get(name);
20
- // js allows undefined > 0
21
- if (setCookie && setCookie.maxAge! > 0) {
22
- return setCookie.value;
20
+ if (setCookie) {
21
+ return (setCookie.maxAge ?? 1) > 0 ? setCookie.value : null;
23
22
  }
24
23
 
25
24
  return this.requestCookies.get(name) ?? null;
@@ -33,6 +32,10 @@ export default class ServerCookies implements Cookies {
33
32
  this.set(name, '', { ...opts, maxAge: 0 });
34
33
  }
35
34
 
35
+ toRequest(): ServerCookies {
36
+ return this;
37
+ }
38
+
36
39
  _populateHeaders(headers: Headers) {
37
40
  for (const setCookie of this.setCookies.values()) {
38
41
  headers.append('Set-Cookie', setCookieToString(setCookie));
@@ -1,29 +1,5 @@
1
- export type SetOptions = {
2
- maxAge?: number;
3
- path?: string;
4
- domain?: string;
5
- secure?: boolean;
6
- httpOnly?: boolean;
7
- };
8
-
9
- export type RemoveOptions = Omit<SetOptions, 'maxAge'>;
10
-
11
- export interface Cookies {
12
- /**
13
- * returns the value of the cookie
14
- */
15
- get(name: string): string | null;
16
-
17
- /**
18
- * sets the value of the cookie
19
- *
20
- * #### Note
21
- * path defaults to '/'
22
- */
23
- set(name: string, value: string, opts?: SetOptions): void;
24
-
25
- /**
26
- * removes the cookie
27
- */
28
- remove(name: string, opts?: RemoveOptions): void;
29
- }
1
+ export {
2
+ default as Cookies,
3
+ type SetOptions,
4
+ type RemoveOptions,
5
+ } from './Cookies.js';
package/src/crelte.ts CHANGED
@@ -9,6 +9,7 @@ import Queries, { Query, QueryOptions } from './queries/Queries.js';
9
9
  import { Readable } from './std/stores/index.js';
10
10
  import { Entry } from './loadData/index.js';
11
11
  import { urlWithPath } from './utils.js';
12
+ import { BodyClass } from './bodyClass/index.js';
12
13
 
13
14
  /** The type for the app.config object */
14
15
  export type Config = {
@@ -113,6 +114,11 @@ export type Crelte = {
113
114
  */
114
115
  cookies: Cookies;
115
116
 
117
+ /**
118
+ * Get the BodyClass instance
119
+ */
120
+ bodyClass: BodyClass;
121
+
116
122
  /**
117
123
  * Get a Plugin by name
118
124
  */
@@ -263,6 +269,7 @@ export function newCrelte({
263
269
  router,
264
270
  queries,
265
271
  cookies,
272
+ bodyClass,
266
273
  }: {
267
274
  config: Required<Config>;
268
275
  ssrCache: SsrCache;
@@ -272,6 +279,7 @@ export function newCrelte({
272
279
  router: Router;
273
280
  queries: Queries;
274
281
  cookies: Cookies;
282
+ bodyClass: BodyClass;
275
283
  }): Crelte {
276
284
  return {
277
285
  config,
@@ -282,6 +290,7 @@ export function newCrelte({
282
290
  router,
283
291
  queries,
284
292
  cookies,
293
+ bodyClass,
285
294
 
286
295
  getPlugin: name => plugins.get(name),
287
296
  getEnv: key => ssrCache.get(key as any) as any,
package/src/index.ts CHANGED
@@ -17,7 +17,8 @@ import {
17
17
  LoadDataObj,
18
18
  } from './loadData/index.js';
19
19
  import Queries from './queries/Queries.js';
20
- import { Readable } from './std/stores/index.js';
20
+ import { Readable, Writable } from './std/stores/index.js';
21
+ import { BodyClass } from './bodyClass/index.js';
21
22
 
22
23
  export {
23
24
  type Crelte,
@@ -33,6 +34,9 @@ export {
33
34
  /** The type for the app.init function */
34
35
  export type Init = (crelte: Crelte) => void;
35
36
 
37
+ /** The Props that are passed to the app */
38
+ export type AppProps = { route: Writable<Route> };
39
+
36
40
  function innerGetCrelte(): Crelte {
37
41
  return getContext('crelte');
38
42
  }
@@ -174,6 +178,16 @@ export function getCookies(): Cookies {
174
178
  return innerGetCrelte().cookies;
175
179
  }
176
180
 
181
+ /**
182
+ * returns the body class instance
183
+ *
184
+ * #### Note
185
+ * This only works during component initialisation.
186
+ */
187
+ export function getBodyClass(): BodyClass {
188
+ return innerGetCrelte().bodyClass;
189
+ }
190
+
177
191
  /**
178
192
  * Listen for route changes
179
193
  *