crelte 0.5.11 → 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 (90) 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.map +1 -1
  32. package/dist/init/client.js +23 -14
  33. package/dist/init/server.d.ts.map +1 -1
  34. package/dist/init/server.js +11 -3
  35. package/dist/init/shared.d.ts +1 -0
  36. package/dist/init/shared.d.ts.map +1 -1
  37. package/dist/init/shared.js +16 -5
  38. package/dist/loadData/Globals.d.ts.map +1 -1
  39. package/dist/node/index.js +1 -1
  40. package/dist/plugins/Events.d.ts +6 -1
  41. package/dist/plugins/Events.d.ts.map +1 -1
  42. package/dist/plugins/Plugins.d.ts +36 -1
  43. package/dist/plugins/Plugins.d.ts.map +1 -1
  44. package/dist/plugins/Plugins.js +32 -0
  45. package/dist/routing/route/Request.d.ts +1 -1
  46. package/dist/routing/route/Request.d.ts.map +1 -1
  47. package/dist/routing/route/Request.js +7 -3
  48. package/dist/routing/router/ClientRouter.d.ts.map +1 -1
  49. package/dist/routing/router/ClientRouter.js +18 -11
  50. package/dist/routing/router/Router.d.ts.map +1 -1
  51. package/dist/routing/router/Router.js +8 -12
  52. package/dist/server/CrelteServer.d.ts +1 -0
  53. package/dist/server/CrelteServer.d.ts.map +1 -1
  54. package/dist/server/CrelteServer.js +5 -2
  55. package/dist/std/stores/StagedWritable.d.ts +48 -0
  56. package/dist/std/stores/StagedWritable.d.ts.map +1 -0
  57. package/dist/std/stores/StagedWritable.js +84 -0
  58. package/dist/std/stores/index.d.ts +2 -1
  59. package/dist/std/stores/index.d.ts.map +1 -1
  60. package/dist/std/stores/index.js +2 -1
  61. package/dist/std/sync/Barrier.js +1 -1
  62. package/dist/utils.d.ts +9 -0
  63. package/dist/utils.d.ts.map +1 -1
  64. package/dist/utils.js +11 -0
  65. package/package.json +5 -1
  66. package/src/bodyClass/BodyClass.ts +72 -0
  67. package/src/bodyClass/ClientBodyClass.ts +62 -0
  68. package/src/bodyClass/ServerBodyClass.ts +65 -0
  69. package/src/bodyClass/index.ts +1 -0
  70. package/src/cookies/ClientCookies.ts +41 -10
  71. package/src/cookies/Cookies.ts +70 -0
  72. package/src/cookies/ServerCookies.ts +9 -6
  73. package/src/cookies/index.ts +5 -29
  74. package/src/crelte.ts +9 -0
  75. package/src/index.ts +15 -1
  76. package/src/init/client.ts +26 -14
  77. package/src/init/server.ts +11 -3
  78. package/src/init/shared.ts +18 -6
  79. package/src/loadData/Globals.ts +1 -1
  80. package/src/node/index.ts +1 -1
  81. package/src/plugins/Events.ts +12 -1
  82. package/src/plugins/Plugins.ts +66 -1
  83. package/src/routing/route/Request.ts +11 -4
  84. package/src/routing/router/ClientRouter.ts +23 -14
  85. package/src/routing/router/Router.ts +8 -10
  86. package/src/server/CrelteServer.ts +4 -2
  87. package/src/std/stores/StagedWritable.ts +96 -0
  88. package/src/std/stores/index.ts +2 -1
  89. package/src/std/sync/Barrier.ts +1 -1
  90. package/src/utils.ts +15 -0
@@ -1,3 +1,4 @@
1
+ import { Cookies } from '../cookies/index.js';
1
2
  import ServerCookies from '../cookies/ServerCookies.js';
2
3
  import { Request } from '../routing/index.js';
3
4
  import { urlWithPath } from '../utils.js';
@@ -11,6 +12,7 @@ export default class CrelteServerRequest {
11
12
  _sites;
12
13
  _langs;
13
14
  _queries;
15
+ _scookies;
14
16
  _cookies;
15
17
  constructor(req, opts) {
16
18
  this.req = req;
@@ -19,7 +21,8 @@ export default class CrelteServerRequest {
19
21
  this._langs = opts.languages;
20
22
  this.prefSite = opts.preferredSite;
21
23
  this._queries = opts.queries.z_toRequest(new Request(new URL(req.url), req.site));
22
- this._cookies = new ServerCookies(req.headers);
24
+ this._scookies = new ServerCookies(req.headers);
25
+ this._cookies = new Cookies(this._scookies);
23
26
  }
24
27
  /**
25
28
  * Easy access to this.req.site
@@ -104,6 +107,6 @@ export default class CrelteServerRequest {
104
107
  }
105
108
  /** @hidden */
106
109
  z_finishResponse(resp) {
107
- this.cookies._populateHeaders(resp.headers);
110
+ this._scookies._populateHeaders(resp.headers);
108
111
  }
109
112
  }
@@ -0,0 +1,48 @@
1
+ import { CloneableOrPrimitive } from '../index.js';
2
+ import { Readable } from './index.js';
3
+ export default class StagedWritable<T> {
4
+ private inner;
5
+ private mode;
6
+ private staged;
7
+ /**
8
+ * Creates a new StagedWritable
9
+ *
10
+ * @param def A default value
11
+ */
12
+ constructor(def: T);
13
+ /**
14
+ * Returns true if the store is currently staged
15
+ */
16
+ isStaged(): boolean;
17
+ /**
18
+ * Returns a new StagedWritable which is staged and has the same value as the current one
19
+ * To commit the staged value, call `commit` on the returned StagedWritable
20
+ */
21
+ stage(): StagedWritable<T>;
22
+ /**
23
+ * The function get's called once with the current value and then when the
24
+ * values changes
25
+ *
26
+ * #### Note
27
+ * This does not check for equality like svelte.
28
+ *
29
+ * @return a function which should be called to unsubscribe
30
+ */
31
+ subscribe(fn: (val: T) => void, invalidate?: () => void): () => void;
32
+ /**
33
+ * Either updates the store and calls all subscribers with the value or
34
+ * updates the stateful value if previously toStateful was called
35
+ */
36
+ set(inner: T): void;
37
+ /**
38
+ * If the value was staged, then update the store and call all subscribers with the value
39
+ */
40
+ commit(): void;
41
+ /**
42
+ * Get the current value either staged or not
43
+ */
44
+ get(): T;
45
+ readonly(): Readable<T>;
46
+ readclone<U extends T & CloneableOrPrimitive>(this: StagedWritable<U>): Readable<U>;
47
+ }
48
+ //# sourceMappingURL=StagedWritable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StagedWritable.d.ts","sourceRoot":"","sources":["../../../src/std/stores/StagedWritable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,MAAM,CAAC,OAAO,OAAO,cAAc,CAAC,CAAC;IACpC,OAAO,CAAC,KAAK,CAAc;IAE3B,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,MAAM,CAAgB;IAE9B;;;;OAIG;gBACS,GAAG,EAAE,CAAC;IAMlB;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;;OAGG;IACH,KAAK,IAAI,cAAc,CAAC,CAAC,CAAC;IAQ1B;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE,UAAU,CAAC,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAIpE;;;OAGG;IACH,GAAG,CAAC,KAAK,EAAE,CAAC;IASZ;;OAEG;IACH,MAAM;IASN;;OAEG;IACH,GAAG,IAAI,CAAC;IAKR,QAAQ,IAAI,QAAQ,CAAC,CAAC,CAAC;IAIvB,SAAS,CAAC,CAAC,SAAS,CAAC,GAAG,oBAAoB,EAC3C,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,GACrB,QAAQ,CAAC,CAAC,CAAC;CAGd"}
@@ -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.11",
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';