@absolutejs/absolute 0.19.0-beta.675 → 0.19.0-beta.676

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.
@@ -0,0 +1,103 @@
1
+ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, NgZone, inject, signal } from '@angular/core';
2
+ import { DomSanitizer } from '@angular/platform-browser';
3
+ import { isStreamingSlotCollectionActive, registerStreamingSlot, warnMissingStreamingSlotCollector } from './core/streamingSlotRegistrar.js';
4
+ import * as i0 from "@angular/core";
5
+ const isObjectRecord = (value) => Boolean(value) && typeof value === 'object';
6
+ const isHtmlPayload = (payload) => isObjectRecord(payload) && typeof payload.html === 'string';
7
+ const resolvePayloadHtml = (payload) => {
8
+ if (isHtmlPayload(payload)) {
9
+ return payload.html;
10
+ }
11
+ return typeof payload === 'string' ? payload : '';
12
+ };
13
+ export class StreamSlotComponent {
14
+ constructor() {
15
+ this.cdr = inject(ChangeDetectorRef);
16
+ this.sanitizer = inject(DomSanitizer);
17
+ this.zone = inject(NgZone);
18
+ this.slotConsumer = (payload) => {
19
+ this.zone.run(() => {
20
+ this.currentHtml.set(this.sanitizer.bypassSecurityTrustHtml(resolvePayloadHtml(payload)));
21
+ this.cdr.markForCheck();
22
+ });
23
+ return true;
24
+ };
25
+ this.fallbackHtml = '';
26
+ this.currentHtml = signal('', ...(ngDevMode ? [{ debugName: "currentHtml" }] : /* istanbul ignore next */ []));
27
+ }
28
+ ngOnInit() {
29
+ if (isStreamingSlotCollectionActive()) {
30
+ this.currentHtml.set(this.sanitizer.bypassSecurityTrustHtml(this.fallbackHtml));
31
+ registerStreamingSlot({
32
+ errorHtml: this.errorHtml,
33
+ fallbackHtml: this.fallbackHtml,
34
+ id: this.id,
35
+ resolve: this.resolve,
36
+ timeoutMs: this.timeoutMs
37
+ });
38
+ return;
39
+ }
40
+ warnMissingStreamingSlotCollector('StreamSlot');
41
+ if (typeof window === 'undefined') {
42
+ this.currentHtml.set(this.sanitizer.bypassSecurityTrustHtml(this.fallbackHtml));
43
+ return;
44
+ }
45
+ const consumers = (window.__ABS_SLOT_CONSUMERS__ =
46
+ window.__ABS_SLOT_CONSUMERS__ ?? {});
47
+ consumers[this.id] = this.slotConsumer;
48
+ this.currentHtml.set(this.sanitizer.bypassSecurityTrustHtml(this.fallbackHtml));
49
+ const pendingPayload = window.__ABS_SLOT_PENDING__?.[this.id];
50
+ if (pendingPayload !== undefined) {
51
+ this.slotConsumer(pendingPayload);
52
+ delete window.__ABS_SLOT_PENDING__?.[this.id];
53
+ }
54
+ }
55
+ ngOnDestroy() {
56
+ if (typeof window === 'undefined')
57
+ return;
58
+ if (window.__ABS_SLOT_CONSUMERS__) {
59
+ delete window.__ABS_SLOT_CONSUMERS__[this.id];
60
+ }
61
+ }
62
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: StreamSlotComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
63
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.6", type: StreamSlotComponent, isStandalone: true, selector: "abs-stream-slot", inputs: { className: "className", errorHtml: "errorHtml", fallbackHtml: "fallbackHtml", id: "id", resolve: "resolve", timeoutMs: "timeoutMs" }, ngImport: i0, template: `
64
+ <div
65
+ [attr.id]="id"
66
+ [attr.class]="className"
67
+ data-absolute-raw-slot="true"
68
+ data-absolute-slot="true"
69
+ [innerHTML]="currentHtml()"
70
+ ></div>
71
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
72
+ }
73
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: StreamSlotComponent, decorators: [{
74
+ type: Component,
75
+ args: [{
76
+ changeDetection: ChangeDetectionStrategy.OnPush,
77
+ selector: 'abs-stream-slot',
78
+ standalone: true,
79
+ template: `
80
+ <div
81
+ [attr.id]="id"
82
+ [attr.class]="className"
83
+ data-absolute-raw-slot="true"
84
+ data-absolute-slot="true"
85
+ [innerHTML]="currentHtml()"
86
+ ></div>
87
+ `
88
+ }]
89
+ }], propDecorators: { className: [{
90
+ type: Input
91
+ }], errorHtml: [{
92
+ type: Input
93
+ }], fallbackHtml: [{
94
+ type: Input
95
+ }], id: [{
96
+ type: Input,
97
+ args: [{ required: true }]
98
+ }], resolve: [{
99
+ type: Input,
100
+ args: [{ required: true }]
101
+ }], timeoutMs: [{
102
+ type: Input
103
+ }] } });
package/dist/cli/index.js CHANGED
@@ -242,9 +242,9 @@ var RESERVED_TOP_LEVEL_KEYS, isObject = (value) => typeof value === "object" &&
242
242
  cwd: _cwd,
243
243
  dependsOn: _dependsOn,
244
244
  env: _env,
245
- healthcheck: _healthcheck,
246
245
  kind: _kind,
247
246
  port: _port,
247
+ ready: _ready,
248
248
  visibility: _visibility,
249
249
  ...serviceConfig
250
250
  } = service;
@@ -284,7 +284,6 @@ var init_loadConfig = __esm(() => {
284
284
  "dev",
285
285
  "entry",
286
286
  "env",
287
- "healthcheck",
288
287
  "htmlDirectory",
289
288
  "htmxDirectory",
290
289
  "images",
@@ -301,6 +300,7 @@ var init_loadConfig = __esm(() => {
301
300
  "stylesConfig",
302
301
  "svelteDirectory",
303
302
  "tailwind",
303
+ "ready",
304
304
  "visibility",
305
305
  "vueDirectory"
306
306
  ]);
@@ -2482,6 +2482,7 @@ init_constants();
2482
2482
  init_loadConfig();
2483
2483
  init_getDurationString();
2484
2484
  import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
2485
+ import { createConnection } from "net";
2485
2486
  import { resolve as resolve6 } from "path";
2486
2487
 
2487
2488
  // src/cli/workspaceTui.ts
@@ -3051,50 +3052,164 @@ var getServiceUrl = (service) => {
3051
3052
  if (!service.port) {
3052
3053
  return null;
3053
3054
  }
3054
- return `http://localhost:${service.port}/`;
3055
+ return `${getServiceProtocol(service)}://${getServicePublicHost(service)}:${service.port}/`;
3055
3056
  };
3056
- var getHealthcheckUrl = (service) => {
3057
- if (service.healthcheck) {
3058
- return service.healthcheck;
3059
- }
3057
+ var getDefaultReadyConfig = (service) => {
3060
3058
  if (isAbsoluteService(service) && service.port) {
3061
- return `http://127.0.0.1:${service.port}/hmr-status`;
3059
+ return "/hmr-status";
3062
3060
  }
3063
3061
  return;
3064
3062
  };
3065
- var resolveHealthcheck = (healthcheck) => {
3066
- if (!healthcheck) {
3067
- return null;
3063
+ var normalizeExpectedStatuses = (value) => Array.isArray(value) ? value : [value ?? 200];
3064
+ var resolveServiceHttpUrl = (service, path) => {
3065
+ if (!path.startsWith("/")) {
3066
+ throw new Error(`ready path must start with "/" for service probes. Received "${path}".`);
3068
3067
  }
3069
- if (typeof healthcheck === "string") {
3068
+ if (!service.port) {
3069
+ throw new Error(`ready path "${path}" requires the service to define a port.`);
3070
+ }
3071
+ return `${getServiceProtocol(service)}://${getServicePublicHost(service)}:${service.port}${path}`;
3072
+ };
3073
+ var resolveHttpReadyProbe = (service, ready) => {
3074
+ if (typeof ready === "string") {
3075
+ if (isAbsoluteService(service)) {
3076
+ return {
3077
+ type: "http",
3078
+ url: resolveServiceHttpUrl(service, ready),
3079
+ method: "GET",
3080
+ expectStatus: [200],
3081
+ headers: {},
3082
+ intervalMs: 250,
3083
+ timeoutMs: 30000
3084
+ };
3085
+ }
3070
3086
  return {
3087
+ type: "http",
3088
+ url: ready,
3089
+ method: "GET",
3090
+ expectStatus: [200],
3091
+ headers: {},
3071
3092
  intervalMs: 250,
3072
- timeoutMs: 30000,
3073
- url: healthcheck
3093
+ timeoutMs: 30000
3074
3094
  };
3075
3095
  }
3096
+ if (ready.path && ready.url) {
3097
+ throw new Error('ready HTTP probe cannot define both "path" and "url".');
3098
+ }
3099
+ const url = ready.path ? resolveServiceHttpUrl(service, ready.path) : ready.url ? ready.url : isAbsoluteService(service) ? resolveServiceHttpUrl(service, "/hmr-status") : null;
3100
+ if (!url) {
3101
+ throw new Error('ready HTTP probe requires either "url" or "path".');
3102
+ }
3076
3103
  return {
3077
- intervalMs: healthcheck.intervalMs ?? 250,
3078
- timeoutMs: healthcheck.timeoutMs ?? 30000,
3079
- url: healthcheck.url
3104
+ type: "http",
3105
+ url,
3106
+ method: ready.method ?? "GET",
3107
+ expectStatus: normalizeExpectedStatuses(ready.expectStatus),
3108
+ headers: ready.headers ?? {},
3109
+ intervalMs: ready.intervalMs ?? 250,
3110
+ timeoutMs: ready.timeoutMs ?? 30000
3080
3111
  };
3081
3112
  };
3082
- var waitForHealthcheck = async (healthcheck) => {
3083
- const resolved = resolveHealthcheck(healthcheck);
3113
+ var resolveReadyProbe = (service, ready = service.ready ?? getDefaultReadyConfig(service)) => {
3114
+ if (ready === false || !ready) {
3115
+ return null;
3116
+ }
3117
+ if (typeof ready === "string") {
3118
+ return resolveHttpReadyProbe(service, ready);
3119
+ }
3120
+ if (ready.type === "tcp") {
3121
+ return {
3122
+ type: "tcp",
3123
+ host: ready.host ?? getServicePublicHost(service),
3124
+ port: ready.port,
3125
+ intervalMs: ready.intervalMs ?? 250,
3126
+ timeoutMs: ready.timeoutMs ?? 30000
3127
+ };
3128
+ }
3129
+ if (ready.type === "command") {
3130
+ return {
3131
+ type: "command",
3132
+ command: ready.command,
3133
+ intervalMs: ready.intervalMs ?? 250,
3134
+ timeoutMs: ready.timeoutMs ?? 30000
3135
+ };
3136
+ }
3137
+ if (ready.type === "delay") {
3138
+ return {
3139
+ type: "delay",
3140
+ ms: ready.ms
3141
+ };
3142
+ }
3143
+ return resolveHttpReadyProbe(service, ready);
3144
+ };
3145
+ var probeHttpReady = async (ready) => {
3146
+ const response = await fetch(ready.url, {
3147
+ method: ready.method,
3148
+ headers: ready.headers,
3149
+ signal: AbortSignal.timeout(Math.min(ready.timeoutMs, 5000))
3150
+ });
3151
+ return ready.expectStatus.includes(response.status);
3152
+ };
3153
+ var probeTcpReady = async (ready) => new Promise((resolveProbe) => {
3154
+ const socket = createConnection({
3155
+ host: ready.host,
3156
+ port: ready.port
3157
+ });
3158
+ const timeout = setTimeout(() => {
3159
+ socket.destroy();
3160
+ resolveProbe(false);
3161
+ }, Math.min(ready.timeoutMs, 5000));
3162
+ socket.once("connect", () => {
3163
+ clearTimeout(timeout);
3164
+ socket.end();
3165
+ resolveProbe(true);
3166
+ });
3167
+ socket.once("error", () => {
3168
+ clearTimeout(timeout);
3169
+ socket.destroy();
3170
+ resolveProbe(false);
3171
+ });
3172
+ });
3173
+ var probeCommandReady = async (ready, service) => {
3174
+ const processHandle = Bun.spawn(ready.command, {
3175
+ cwd: service.cwd,
3176
+ env: service.env,
3177
+ stderr: "ignore",
3178
+ stdin: "ignore",
3179
+ stdout: "ignore"
3180
+ });
3181
+ const timeout = setTimeout(() => {
3182
+ try {
3183
+ processHandle.kill();
3184
+ } catch {}
3185
+ }, Math.min(ready.timeoutMs, 5000));
3186
+ try {
3187
+ const exitCode = await processHandle.exited;
3188
+ return exitCode === 0;
3189
+ } finally {
3190
+ clearTimeout(timeout);
3191
+ }
3192
+ };
3193
+ var waitForReady = async (service) => {
3194
+ const resolved = resolveReadyProbe(service.service);
3084
3195
  if (!resolved) {
3085
3196
  return;
3086
3197
  }
3198
+ if (resolved.type === "delay") {
3199
+ await sleep(resolved.ms);
3200
+ return;
3201
+ }
3087
3202
  const startedAt = Date.now();
3088
3203
  while (Date.now() - startedAt < resolved.timeoutMs) {
3089
3204
  try {
3090
- const response = await fetch(resolved.url);
3091
- if (response.ok) {
3205
+ const isReady = resolved.type === "http" ? await probeHttpReady(resolved) : resolved.type === "tcp" ? await probeTcpReady(resolved) : await probeCommandReady(resolved, service);
3206
+ if (isReady) {
3092
3207
  return;
3093
3208
  }
3094
3209
  } catch {}
3095
3210
  await sleep(resolved.intervalMs);
3096
3211
  }
3097
- throw new Error(`service did not become healthy within ${resolved.timeoutMs}ms (${resolved.url})`);
3212
+ throw new Error(resolved.type === "http" ? `service did not become ready within ${resolved.timeoutMs}ms (${resolved.url})` : resolved.type === "tcp" ? `service did not become ready within ${resolved.timeoutMs}ms (tcp://${resolved.host}:${resolved.port})` : `service did not become ready within ${resolved.timeoutMs}ms (${resolved.command.join(" ")})`);
3098
3213
  };
3099
3214
  var topologicallySortServices = (services) => {
3100
3215
  const ordered = [];
@@ -3341,7 +3456,7 @@ var workspace = async (subcommand, options) => {
3341
3456
  tui.addLog("workspace", `${name} exited with code ${exitCode || 1}. Shutting down workspace.`, "error");
3342
3457
  shutdown(exitCode || 1);
3343
3458
  });
3344
- await waitForHealthcheck(getHealthcheckUrl(resolved.service));
3459
+ await waitForReady(resolved);
3345
3460
  const startedAt = serviceBootStartedAt.get(name);
3346
3461
  const readyDuration = typeof startedAt === "number" ? `ready in ${getDurationString(performance.now() - startedAt)}` : undefined;
3347
3462
  tui.setServiceStatus(name, "ready", readyDuration);
package/dist/index.js CHANGED
@@ -181936,7 +181936,6 @@ var RESERVED_TOP_LEVEL_KEYS = new Set([
181936
181936
  "dev",
181937
181937
  "entry",
181938
181938
  "env",
181939
- "healthcheck",
181940
181939
  "htmlDirectory",
181941
181940
  "htmxDirectory",
181942
181941
  "images",
@@ -181953,6 +181952,7 @@ var RESERVED_TOP_LEVEL_KEYS = new Set([
181953
181952
  "stylesConfig",
181954
181953
  "svelteDirectory",
181955
181954
  "tailwind",
181955
+ "ready",
181956
181956
  "visibility",
181957
181957
  "vueDirectory"
181958
181958
  ]);
@@ -181993,9 +181993,9 @@ var projectServiceConfig = (config, serviceName) => {
181993
181993
  cwd: _cwd,
181994
181994
  dependsOn: _dependsOn,
181995
181995
  env: _env,
181996
- healthcheck: _healthcheck,
181997
181996
  kind: _kind,
181998
181997
  port: _port,
181998
+ ready: _ready,
181999
181999
  visibility: _visibility,
182000
182000
  ...serviceConfig
182001
182001
  } = service;
@@ -188898,5 +188898,5 @@ export {
188898
188898
  ANGULAR_INIT_TIMEOUT_MS
188899
188899
  };
188900
188900
 
188901
- //# debugId=5420DB14363AEF7064756E2164756E21
188901
+ //# debugId=97D769D373BB628F64756E2164756E21
188902
188902
  //# sourceMappingURL=index.js.map