@webqit/webflo 0.20.4-next.1 → 0.20.4-next.3

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 (46) hide show
  1. package/package.json +5 -20
  2. package/site/.vitepress/config.ts +1 -1
  3. package/site/docs/concepts/realtime.md +57 -53
  4. package/site/docs/concepts/{request-response.md → requests-responses.md} +1 -1
  5. package/site/docs/concepts/state.md +1 -1
  6. package/site/docs/getting-started.md +40 -40
  7. package/src/{Context.js → CLIContext.js} +9 -8
  8. package/src/build-pi/esbuild-plugin-livejs-transform.js +35 -0
  9. package/src/{runtime-pi/webflo-client/webflo-codegen.js → build-pi/index.js} +145 -141
  10. package/src/index.js +3 -1
  11. package/src/init-pi/index.js +6 -3
  12. package/src/init-pi/templates/pwa/package.json +2 -2
  13. package/src/init-pi/templates/web/package.json +2 -2
  14. package/src/runtime-pi/AppBootstrap.js +38 -0
  15. package/src/runtime-pi/WebfloRuntime.js +50 -47
  16. package/src/runtime-pi/apis.js +9 -0
  17. package/src/runtime-pi/index.js +2 -4
  18. package/src/runtime-pi/webflo-client/WebfloClient.js +31 -35
  19. package/src/runtime-pi/webflo-client/WebfloRootClient1.js +16 -14
  20. package/src/runtime-pi/webflo-client/WebfloSubClient.js +13 -13
  21. package/src/runtime-pi/webflo-client/bootstrap.js +37 -0
  22. package/src/runtime-pi/webflo-client/index.js +2 -8
  23. package/src/runtime-pi/webflo-client/webflo-devmode.js +3 -3
  24. package/src/runtime-pi/webflo-fetch/LiveResponse.js +127 -96
  25. package/src/runtime-pi/webflo-fetch/index.js +435 -5
  26. package/src/runtime-pi/webflo-routing/HttpCookies.js +1 -1
  27. package/src/runtime-pi/webflo-routing/HttpEvent.js +5 -6
  28. package/src/runtime-pi/webflo-routing/HttpUser.js +7 -7
  29. package/src/runtime-pi/webflo-server/ServerSideCookies.js +3 -1
  30. package/src/runtime-pi/webflo-server/ServerSideSession.js +2 -1
  31. package/src/runtime-pi/webflo-server/WebfloServer.js +98 -195
  32. package/src/runtime-pi/webflo-server/bootstrap.js +59 -0
  33. package/src/runtime-pi/webflo-server/index.js +2 -6
  34. package/src/runtime-pi/webflo-server/webflo-devmode.js +13 -24
  35. package/src/runtime-pi/webflo-worker/WebfloWorker.js +11 -15
  36. package/src/runtime-pi/webflo-worker/WorkerSideCookies.js +2 -1
  37. package/src/runtime-pi/webflo-worker/bootstrap.js +38 -0
  38. package/src/runtime-pi/webflo-worker/index.js +3 -7
  39. package/src/webflo-cli.js +1 -2
  40. package/src/runtime-pi/webflo-fetch/cookies.js +0 -10
  41. package/src/runtime-pi/webflo-fetch/fetch.js +0 -16
  42. package/src/runtime-pi/webflo-fetch/formdata.js +0 -54
  43. package/src/runtime-pi/webflo-fetch/headers.js +0 -151
  44. package/src/runtime-pi/webflo-fetch/message.js +0 -49
  45. package/src/runtime-pi/webflo-fetch/request.js +0 -62
  46. package/src/runtime-pi/webflo-fetch/response.js +0 -110
@@ -1,5 +1,435 @@
1
- import './formdata.js';
2
- import './request.js';
3
- import './response.js';
4
- import './LiveResponse.js';
5
- import './headers.js';
1
+ export { LiveResponse } from './LiveResponse.js';
2
+ import { _isObject, _isTypeObject, _isNumeric } from '@webqit/util/js/index.js';
3
+ import { _from as _arrFrom } from '@webqit/util/arr/index.js';
4
+ import { _before, _after } from '@webqit/util/str/index.js';
5
+ import { DeepURLSearchParams } from '../webflo-url/util.js';
6
+ import { dataType } from './util.js';
7
+ import { _wq } from '../../util.js';
8
+
9
+ // ----- env & globalize
10
+
11
+ export const env = {};
12
+
13
+ export function shim(prefix = 'wq') {
14
+ const apis = [Request, Response, Headers, FormData];
15
+ const descs = [request, response, headers, formData];
16
+ const patch = (api, desc) => {
17
+ const _descs = Object.fromEntries(Object.entries(desc).map(([key, value]) => {
18
+ if (prefix && key in api) {
19
+ key = `${prefix}${key[0].toUpperCase()}${key.slice(1)}`;
20
+ }
21
+ return [key, value];
22
+ }));
23
+ Object.defineProperties(api, _descs);
24
+ };
25
+ for (let i = 0; i < apis.length; i++) {
26
+ const api = apis[i];
27
+ const { prototype, ...statics } = descs[i];
28
+ patch(api, statics);
29
+ if (prototype) {
30
+ patch(api.prototype, prototype);
31
+ }
32
+ }
33
+ }
34
+
35
+ // ----- request
36
+
37
+ const requestOriginals = { prototype: { clone: Request.prototype.clone } };
38
+
39
+ export const request = {
40
+ from: {
41
+ value: function (url, init = {}) {
42
+ if (url instanceof Request) return url;
43
+ let $$type, $$body = init.body;
44
+ if ('body' in init) {
45
+ const { body, headers, $type } = renderHttpMessageInit(init);
46
+ init = { ...init, body, headers };
47
+ $$type = $type;
48
+ }
49
+ const instance = new Request(url, init);
50
+ const responseMeta = _wq(instance, 'meta');
51
+ responseMeta.set('body', $$body);
52
+ responseMeta.set('type', $$type);
53
+ return instance;
54
+ }
55
+ },
56
+ copy: {
57
+ value: async function (request, init = {}) {
58
+ const attrs = ['method', 'headers', 'mode', 'credentials', 'cache', 'redirect', 'referrer', 'integrity'];
59
+ const requestInit = attrs.reduce(($init, prop) => (
60
+ {
61
+ ...$init,
62
+ [prop]: prop in init
63
+ ? init[prop]
64
+ : (prop === 'headers'
65
+ ? new Headers(request[prop])
66
+ : request[prop])
67
+ }
68
+ ), {});
69
+ if (!['GET', 'HEAD'].includes(init.method?.toUpperCase() || request.method)) {
70
+ if ('body' in init) {
71
+ requestInit.body = init.body
72
+ if (!('headers' in init)) {
73
+ requestInit.headers.delete('Content-Type');
74
+ requestInit.headers.delete('Content-Length');
75
+ }
76
+ } else {
77
+ requestInit.body = await request.clone().arrayBuffer();
78
+ }
79
+ }
80
+ if (requestInit.mode === 'navigate') {
81
+ requestInit.mode = 'cors';
82
+ }
83
+ return { url: request.url, ...requestInit };
84
+ }
85
+ },
86
+ prototype: {
87
+ carries: { get: function () { return new Set(_wq(this, 'meta').get('carries') || []); } },
88
+ parse: { value: async function () { return await parseHttpMessage(this); } },
89
+ clone: {
90
+ value: function (init = {}) {
91
+ const clone = requestOriginals.prototype.clone.call(this, init);
92
+ const requestMeta = _wq(this, 'meta');
93
+ _wq(clone).set('meta', requestMeta);
94
+ return clone;
95
+ }
96
+ },
97
+ }
98
+ };
99
+
100
+ // ----- response
101
+
102
+ const responseOriginals = {
103
+ json: Response.json,
104
+ prototype: {
105
+ status: Object.getOwnPropertyDescriptor(Response.prototype, 'status'),
106
+ clone: Response.prototype.clone,
107
+ },
108
+ };
109
+
110
+ export const response = {
111
+ json: {
112
+ value: function (data, options = {}) {
113
+ const instance = responseOriginals.json(data, options);
114
+ const responseMeta = _wq(instance, 'meta');
115
+ responseMeta.set('body', data);
116
+ responseMeta.set('type', 'json');
117
+ return instance;
118
+ }
119
+ },
120
+ from: {
121
+ value: function (body, init = {}) {
122
+ if (body instanceof Response) return body;
123
+ let $type, $body = body;
124
+ if (body || body === 0) {
125
+ let headers;
126
+ ({ body, headers, $type } = renderHttpMessageInit({ body, headers: init.headers }));
127
+ init = { ...init, headers };
128
+ }
129
+ const instance = new Response(body, init);
130
+ const responseMeta = _wq(instance, 'meta');
131
+ responseMeta.set('body', $body);
132
+ responseMeta.set('type', $type);
133
+ return instance;
134
+ }
135
+ },
136
+ redirectWith: {
137
+ value: function (url, { status = 302, request = null, response = null }) {
138
+ if (typeof status !== 'string') {
139
+ throw new Error('Redirect code must be an object!');
140
+ }
141
+ if (request && !_isObject(request) || response && !_isObject(response)) {
142
+ throw new Error('Carries (redirect requests and responses) must be an object!');
143
+ }
144
+ const responseInstance = this.redirect(url, status);
145
+ if (request || response) {
146
+ const responseMeta = _wq(responseInstance, 'meta');
147
+ responseMeta.set('carry', { request, response });
148
+ }
149
+ return responseInstance;
150
+ }
151
+ },
152
+ prototype: {
153
+ status: { get: function () { return _wq(this, 'meta').get('status') || responseOriginals.prototype.status.get.call(this); } },
154
+ carry: { get: function () { return _wq(this, 'meta').get('carry'); } },
155
+ parse: { value: async function () { return await parseHttpMessage(this); } },
156
+ clone: {
157
+ value: function (init = {}) {
158
+ const clone = responseOriginals.prototype.clone.call(this, init);
159
+ const responseMeta = _wq(this, 'meta');
160
+ _wq(clone).set('meta', responseMeta);
161
+ return clone;
162
+ }
163
+ },
164
+ }
165
+ };
166
+
167
+ // ----- headers
168
+
169
+ const headersOriginals = {
170
+ set: Headers.prototype.set,
171
+ get: Headers.prototype.get,
172
+ append: Headers.prototype.append,
173
+ };
174
+
175
+ export const headers = {
176
+ set: {
177
+ value: function (name, value) {
178
+
179
+ // Format "Set-Cookie" response header
180
+ if (/^Set-Cookie$/i.test(name) && _isObject(value)) {
181
+ value = renderCookieObjToString(value);
182
+ }
183
+
184
+ // Format "Cookie" request header
185
+ if (/Cookie/i.test(name) && _isTypeObject(value)) {
186
+ value = [].concat(value).map(renderCookieObjToString).join(';');
187
+ }
188
+
189
+ // Format "Content-Range" response header?
190
+ if (/^Content-Range$/i.test(name) && Array.isArray(value)) {
191
+ if (value.length < 2 || !value[0].includes('-')) {
192
+ throw new Error(`A Content-Range array must be in the format: [ 'start-end', 'total' ]`);
193
+ }
194
+ value = `bytes ${value.join('/')}`;
195
+ }
196
+
197
+ // Format "Range" request header?
198
+ if (/^Range$/i.test(name)) {
199
+ let rangeArr = [];
200
+ _arrFrom(value).forEach((range, i) => {
201
+ let rangeStr = Array.isArray(range) ? range.join('-') : range + '';
202
+ if (i === 0 && !rangeStr.includes('bytes=')) {
203
+ rangeStr = `bytes=${rangeStr}`;
204
+ }
205
+ rangeArr.push(rangeStr);
206
+ });
207
+ value = rangeArr.join(', ');
208
+ }
209
+
210
+ // Format "Accept" request header?
211
+ if (/^Accept$/i.test(name) && Array.isArray(value)) {
212
+ value = value.join(',');
213
+ }
214
+
215
+ return headersOriginals.set.call(this, name, value);
216
+ }
217
+ },
218
+ append: {
219
+ value: function (name, value) {
220
+
221
+ // Format "Set-Cookie" response header
222
+ if (/^Set-Cookie$/i.test(name) && _isObject(value)) {
223
+ value = renderCookieObjToString(value);
224
+ }
225
+
226
+ return headersOriginals.append.call(this, name, value);
227
+ }
228
+ },
229
+ get: {
230
+ value: function (name, parsed = false) {
231
+ let value = headersOriginals.get.call(this, name);
232
+
233
+ // Parse "Set-Cookie" response header
234
+ if (/^Set-Cookie$/i.test(name) && parsed) {
235
+ value = this.getSetCookie()/*IMPORTANT*/.map((str) => {
236
+ const [cookieDefinition, attrsStr] = str.split(';');
237
+ const [name, value] = cookieDefinition.split('=').map((s) => s.trim());
238
+ const cookieObj = { name, value: /*decodeURIComponent*/(value), };
239
+ attrsStr && attrsStr.split(/\;/g).map(attrStr => attrStr.trim().split('=')).forEach(attrsArr => {
240
+ cookieObj[attrsArr[0][0].toLowerCase() + attrsArr[0].substring(1).replace('-', '')] = attrsArr.length === 1 ? true : attrsArr[1];
241
+ });
242
+ return cookieObj;
243
+ });
244
+ }
245
+
246
+ // Parse "Cookie" request header
247
+ if (/^Cookie$/i.test(name) && parsed) {
248
+ value = value?.split(';').map((str) => {
249
+ const [name, value] = str.split('=').map((s) => s.trim());
250
+ return { name, value: /*decodeURIComponent*/(value), };
251
+ }) || [];
252
+ }
253
+
254
+ // Parse "Content-Range" response header?
255
+ if (/^Content-Range$/i.test(name) && value && parsed) {
256
+ value = _after(value, 'bytes ').split('/');
257
+ }
258
+
259
+ // Parse "Range" request header?
260
+ if (/^Range$/i.test(name) && parsed) {
261
+ value = !value ? [] : _after(value, 'bytes=').split(',').map((rangeStr) => {
262
+ const range = rangeStr.trim().split('-').map((s) => s ? parseInt(s, 10) : null);
263
+ range.render = (totalLength) => {
264
+ if (range[1] === null) {
265
+ range[1] = totalLength - 1;
266
+ }
267
+ if (range[0] === null) {
268
+ range[0] = range[1] ? totalLength - range[1] - 1 : 0;
269
+ }
270
+ return range
271
+ };
272
+ range.isValid = (currentStart, totalLength) => {
273
+ // Start higher than end or vice versa?
274
+ if (range[0] > range[1] || range[1] < range[0]) return false;
275
+ // Stretching beyond valid start/end?
276
+ if (range[0] < currentStart || range[1] > totalLength) return false;
277
+ return true;
278
+ };
279
+ return range;
280
+ });
281
+ }
282
+
283
+ // Parse "Accept" request header?
284
+ if (/^Accept$/i.test(name) && value && parsed) {
285
+ const parseSpec = (spec) => {
286
+ const [mime, q] = spec.trim().split(';').map((s) => s.trim());
287
+ return [mime, parseFloat((q || 'q=1').replace('q=', ''))];
288
+ };
289
+ const list = value.split(',')
290
+ .map((spec) => parseSpec(spec))
291
+ .sort((a, b) => a[1] > b[1] ? -1 : 1) || [];
292
+ const $value = value;
293
+ value = {
294
+ match(mime) {
295
+ if (!mime) return 0;
296
+ const splitMime = (mime) => mime.split('/').map((s) => s.trim());
297
+ const $mime = splitMime(mime + '');
298
+ return list.reduce((prev, [entry, q]) => {
299
+ if (prev) return prev;
300
+ const $entry = splitMime(entry);
301
+ return [0, 1].every((i) => (($mime[i] === $entry[i]) || $mime[i] === '*' || $entry[i] === '*')) ? q : 0;
302
+ }, 0);
303
+ },
304
+ toString() {
305
+ return $value;
306
+ }
307
+ };
308
+ }
309
+
310
+ return value;
311
+ }
312
+ }
313
+ };
314
+
315
+ // ----- formData
316
+
317
+ export const formData = {
318
+ json: { value: createFormDataFromJson },
319
+ prototype: {
320
+ json: {
321
+ value: async function (data = {}) {
322
+ const result = await renderFormDataToJson(this, ...arguments);
323
+ return result;
324
+ }
325
+ }
326
+ }
327
+ };
328
+
329
+ // ----- Utils
330
+
331
+ export function renderHttpMessageInit(httpMessageInit) {
332
+ // JSONfy headers
333
+ const headers = (httpMessageInit.headers instanceof Headers) ? [...httpMessageInit.headers.entries()].reduce((_headers, [name, value]) => {
334
+ return { ..._headers, [name/* lower-cased */]: _headers[name] ? [].concat(_headers[name], value) : value };
335
+ }, {}) : Object.keys(httpMessageInit.headers || {}).reduce((_headers, name) => {
336
+ return { ..._headers, [name.toLowerCase()]: httpMessageInit.headers[name] };
337
+ }, {});
338
+ // Process body
339
+ let body = httpMessageInit.body, type = dataType(httpMessageInit.body);
340
+ if (['Blob', 'File'].includes(type)) {
341
+ !headers['content-type'] && (headers['content-type'] = body.type);
342
+ !headers['content-length'] && (headers['content-length'] = body.size);
343
+ } else if (['Uint8Array', 'Uint16Array', 'Uint32Array', 'ArrayBuffer'].includes(type)) {
344
+ !headers['content-length'] && (headers['content-length'] = body.byteLength);
345
+ } else if (type === 'json' && _isTypeObject(body)/*JSON object*/) {
346
+ if (!headers['content-type']) {
347
+ const [_body, isJsonfiable] = createFormDataFromJson(body, true/*jsonfy*/, true/*getIsJsonfiable*/);
348
+ if (isJsonfiable) {
349
+ body = JSON.stringify(body, (k, v) => v instanceof Error ? { ...v, message: v.message } : v);
350
+ headers['content-type'] = 'application/json';
351
+ headers['content-length'] = (new Blob([body])).size;
352
+ } else {
353
+ body = _body;
354
+ type = 'FormData';
355
+ }
356
+ }
357
+ } else if (type === 'json'/*JSON string*/ && !headers['content-length']) {
358
+ (headers['content-length'] = (body + '').length);
359
+ }
360
+ return { body, headers, $type: type };
361
+ }
362
+
363
+ export async function parseHttpMessage(httpMessage) {
364
+ let result;
365
+ const contentType = httpMessage.headers.get('Content-Type') || '';
366
+ if (contentType === 'application/x-www-form-urlencoded' || contentType.startsWith('multipart/form-data')) {
367
+ const fd = await httpMessage.formData();
368
+ result = fd && await formData.json.value(fd);
369
+ } else if (contentType.startsWith('application/json')/*can include charset*/) {
370
+ result = await httpMessage.json();
371
+ } else /*if (contentType === 'text/plain')*/ {
372
+ result = httpMessage.body;
373
+ }
374
+ return result;
375
+ }
376
+
377
+ // -----
378
+
379
+ export function createFormDataFromJson(data = {}, jsonfy = true, getIsJsonfiable = false) {
380
+ const formData = new FormData;
381
+ let isJsonfiable = true;
382
+ DeepURLSearchParams.reduceValue(data, '', (value, contextPath, suggestedKeys = undefined) => {
383
+ if (suggestedKeys) {
384
+ const isJson = dataType(value) === 'json';
385
+ isJsonfiable = isJsonfiable && isJson;
386
+ return isJson && suggestedKeys;
387
+ }
388
+ if (jsonfy && [true, false, null].includes(value)) {
389
+ value = new Blob([value], { type: 'application/json' });
390
+ }
391
+ formData.append(contextPath, value);
392
+ });
393
+ if (getIsJsonfiable) return [formData, isJsonfiable];
394
+ return formData;
395
+ }
396
+
397
+ export async function renderFormDataToJson(formData, jsonfy = true, getIsJsonfiable = false) {
398
+ let isJsonfiable = true;
399
+ let json;
400
+ for (let [name, value] of formData.entries()) {
401
+ if (!json) { json = _isNumeric(_before(name, '[')) ? [] : {}; }
402
+ let type = dataType(value);
403
+ if (jsonfy && ['Blob', 'File'].includes(type) && value.type === 'application/json') {
404
+ let _value = await value.text();
405
+ value = JSON.parse(_value);
406
+ type = 'json';
407
+ }
408
+ isJsonfiable = isJsonfiable && type === 'json';
409
+ DeepURLSearchParams.set(json, name, value);
410
+ }
411
+ if (getIsJsonfiable) return [json, isJsonfiable];
412
+ return json;
413
+ }
414
+
415
+ // -----
416
+
417
+ export function renderCookieObjToString(cookieObj) {
418
+ const attrsArr = [`${cookieObj.name}=${/*encodeURIComponent*/(cookieObj.value)}`];
419
+ for (const attrName in cookieObj) {
420
+ if (['name', 'value'].includes(attrName)) continue;
421
+ let _attrName = attrName[0].toUpperCase() + attrName.substring(1);
422
+ if (_attrName === 'MaxAge') { _attrName = 'Max-Age' };
423
+ attrsArr.push(cookieObj[attrName] === true ? _attrName : `${_attrName}=${cookieObj[attrName]}`);
424
+ }
425
+ return attrsArr.join(';');
426
+ }
427
+
428
+ // ----- shim
429
+
430
+ const importUrl = new URL(import.meta.url);
431
+ if (importUrl.searchParams.has('shim')) {
432
+ globalThis.LiveResponse = LiveResponse;
433
+ shim(importUrl.searchParams.get('shim')?.trim());
434
+ console.log('Webflo Fetch APIs shimmed.');
435
+ }
@@ -1,5 +1,5 @@
1
1
  import { _isObject } from '@webqit/util/js/index.js';
2
- import { renderCookieObjToString } from '../webflo-fetch/cookies.js';
2
+ import { renderCookieObjToString } from '../webflo-fetch/index.js';
3
3
  import { HttpState } from './HttpState.js';
4
4
 
5
5
  export class HttpCookies extends HttpState {
@@ -1,5 +1,6 @@
1
- import { _difference } from '@webqit/util/arr/index.js';
2
1
  import { _isObject } from '@webqit/util/js/index.js';
2
+ import { _difference } from '@webqit/util/arr/index.js';
3
+ import { LiveResponse } from '../webflo-fetch/index.js';
3
4
  import { xURL } from '../webflo-url/xURL.js';
4
5
  import { _wq } from '../../util.js';
5
6
 
@@ -14,9 +15,9 @@ export class HttpEvent {
14
15
  #init;
15
16
  #abortController = new AbortController;
16
17
 
17
- constructor(parentEvent, { request, cookies, session, user, realtime, sdk, detail, signal, state, ...rest }) {
18
+ constructor(parentEvent, { request, cookies, session, user, client, detail, signal, state, ...rest }) {
18
19
  this.#parentEvent = parentEvent;
19
- this.#init = { request, cookies, session, user, realtime, sdk, detail, signal, state, ...rest };
20
+ this.#init = { request, cookies, session, user, client, detail, signal, state, ...rest };
20
21
  this.#url = new xURL(this.#init.request.url);
21
22
  this.#parentEvent?.signal.addEventListener('abort', () => this.#abortController.abort(), { once: true });
22
23
  this.#init.request.signal?.addEventListener('abort', () => this.#abortController.abort(), { once: true });
@@ -29,7 +30,7 @@ export class HttpEvent {
29
30
 
30
31
  get request() { return this.#init.request; }
31
32
 
32
- get realtime() { return this.#init.realtime; }
33
+ get client() { return this.#init.client; }
33
34
 
34
35
  get cookies() { return this.#init.cookies; }
35
36
 
@@ -37,8 +38,6 @@ export class HttpEvent {
37
38
 
38
39
  get user() { return this.#init.user; }
39
40
 
40
- get sdk() { return this.#init.sdk; }
41
-
42
41
  get detail() { return this.#init.detail; }
43
42
 
44
43
  get signal() { return this.#abortController.signal; }
@@ -2,19 +2,19 @@ import { HttpState } from './HttpState.js';
2
2
 
3
3
  export class HttpUser extends HttpState {
4
4
 
5
- static create({ store, request, realtime, session }) {
6
- return new this({ store, request, realtime, session });
5
+ static create({ store, request, client, session }) {
6
+ return new this({ store, request, client, session });
7
7
  }
8
8
 
9
- #realtime;
9
+ #client;
10
10
 
11
- constructor({ store, request, realtime, session }) {
11
+ constructor({ store, request, client, session }) {
12
12
  super({
13
13
  store,
14
14
  request,
15
15
  session
16
16
  });
17
- this.#realtime = realtime;
17
+ this.#client = client;
18
18
  }
19
19
 
20
20
  async isSignedIn() {
@@ -34,7 +34,7 @@ export class HttpUser extends HttpState {
34
34
 
35
35
  async confirm(data, callback, options = {}) {
36
36
  return await new Promise((resolve) => {
37
- this.#realtime.postRequest(
37
+ this.#client.postRequest(
38
38
  data,
39
39
  (event) => resolve(callback ? callback(event) : event),
40
40
  { ...options, wqEventOptions: { type: 'confirm' } }
@@ -44,7 +44,7 @@ export class HttpUser extends HttpState {
44
44
 
45
45
  async prompt(data, callback, options = {}) {
46
46
  return await new Promise((resolve) => {
47
- this.#realtime.postRequest(
47
+ this.#client.postRequest(
48
48
  data,
49
49
  (event) => resolve(callback ? callback(event) : event),
50
50
  { ...options, wqEventOptions: { type: 'prompt' } }
@@ -1,10 +1,12 @@
1
+ import { headers as headersShim } from '../webflo-fetch/index.js';
1
2
  import { HttpCookies } from '../webflo-routing/HttpCookies.js';
2
3
 
3
4
  export class ServerSideCookies extends HttpCookies {
4
5
  static create({ request }) {
6
+ const cookies = headersShim.get.value.call(request.headers, 'Cookie', true);
5
7
  return new this({
6
8
  request,
7
- entries: request.headers.get('Cookie', true).map((c) => [c.name, c])
9
+ entries: cookies.map((c) => [c.name, c])
8
10
  });
9
11
  }
10
12
 
@@ -1,4 +1,5 @@
1
1
  import { HttpSession } from '../webflo-routing/HttpSession.js';
2
+ import { headers as headersShim } from '../webflo-fetch/index.js';
2
3
 
3
4
  export class ServerSideSession extends HttpSession {
4
5
 
@@ -29,7 +30,7 @@ export class ServerSideSession extends HttpSession {
29
30
  }
30
31
 
31
32
  async commit(response = null, devMode = false) {
32
- if (response && !response.headers.get('Set-Cookie', true).find((c) => c.name === '__sessid')) {
33
+ if (response && !headersShim.get.value.call(response.headers, 'Set-Cookie', true).find((c) => c.name === '__sessid')) {
33
34
  // expires six months
34
35
  response.headers.append('Set-Cookie', `__sessid=${this.#sessionID}; Path=/; ${!devMode ? 'Secure; ' : ''}HttpOnly; SameSite=Lax${this.#ttl ? `; Max-Age=${this.#ttl}` : ''}`);
35
36
  }