@yourrealm/sdk 0.0.0-alpha.1378.ce03c8dc
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.
- package/LICENSE-EXCEPTION +26 -0
- package/README.md +34 -0
- package/dist/chunk-3R6KEFOI.js +22 -0
- package/dist/chunk-67BDAMYX.js +376 -0
- package/dist/chunk-AITGS27R.js +139 -0
- package/dist/chunk-CMAEHQ4F.js +6 -0
- package/dist/chunk-MLKGABMK.js +9 -0
- package/dist/chunk-O3UIK34B.js +1430 -0
- package/dist/chunk-VHTUSZUX.js +176 -0
- package/dist/cli.js +9006 -0
- package/dist/docker.js +54 -0
- package/dist/index.js +1053 -0
- package/dist/ingress.js +436 -0
- package/dist/jsx-runtime.js +2 -0
- package/dist/preact.js +22 -0
- package/dist/service-url.js +11 -0
- package/dist/services.js +213 -0
- package/dist/test.js +5069 -0
- package/dist/ui.js +254 -0
- package/dist/worker-protocol.js +89 -0
- package/package.json +112 -0
- package/src/app-icon.ts +74 -0
- package/src/apply.ts +52 -0
- package/src/bundle-ui.ts +211 -0
- package/src/bundle.ts +546 -0
- package/src/cli/build-dev-landing.ts +84 -0
- package/src/cli/build-install-panel.ts +90 -0
- package/src/cli/build-runtime.ts +133 -0
- package/src/cli/build.ts +63 -0
- package/src/cli/dev.ts +107 -0
- package/src/cli/down.ts +60 -0
- package/src/cli/main.ts +56 -0
- package/src/cli/test.ts +61 -0
- package/src/cli/validate.ts +37 -0
- package/src/compile.ts +100 -0
- package/src/create-app.ts +982 -0
- package/src/deep-merge.ts +33 -0
- package/src/dev/fixture.ts +48 -0
- package/src/dev/install-handler.ts +383 -0
- package/src/dev/server.ts +968 -0
- package/src/dev/stack.ts +108 -0
- package/src/dev/state.ts +159 -0
- package/src/docker.ts +96 -0
- package/src/errors.ts +61 -0
- package/src/factory.ts +481 -0
- package/src/garage.ts +56 -0
- package/src/generator/hash.ts +6 -0
- package/src/generator/resolve-refs.ts +207 -0
- package/src/grants-builder.ts +100 -0
- package/src/grants.ts +260 -0
- package/src/home-api.ts +172 -0
- package/src/home-cookie.ts +10 -0
- package/src/home-registry.ts +120 -0
- package/src/home.ts +187 -0
- package/src/host-registry.ts +143 -0
- package/src/http-header.ts +20 -0
- package/src/identifiers.ts +49 -0
- package/src/index.ts +66 -0
- package/src/info.ts +64 -0
- package/src/ini.ts +50 -0
- package/src/json.ts +14 -0
- package/src/jsx-runtime/index.ts +5 -0
- package/src/keys.ts +29 -0
- package/src/mysql.ts +56 -0
- package/src/pack-build.ts +108 -0
- package/src/pack.ts +115 -0
- package/src/peer-registry.ts +121 -0
- package/src/peer-world.ts +117 -0
- package/src/permission-meta.ts +116 -0
- package/src/permissions.ts +108 -0
- package/src/pkg-root.ts +34 -0
- package/src/postgres.ts +79 -0
- package/src/preact/index.ts +27 -0
- package/src/redis.ts +43 -0
- package/src/ref.ts +271 -0
- package/src/remote-registry.ts +33 -0
- package/src/required-host-kinds.ts +110 -0
- package/src/resolve-context.ts +111 -0
- package/src/resolve.ts +471 -0
- package/src/runtime/docker/compose/compose-schema.ts +118 -0
- package/src/runtime/docker/compose/convert-to-docker-compose.ts +278 -0
- package/src/runtime/docker/compose/default-hardening.ts +21 -0
- package/src/runtime/docker/compose/effects.ts +107 -0
- package/src/runtime/docker/compose/index.ts +17 -0
- package/src/runtime/docker/compose/labels.ts +39 -0
- package/src/runtime/docker/compose/types.ts +78 -0
- package/src/runtime/docker/home-proxy-server/index.ts +177 -0
- package/src/runtime/docker/home-proxy-server/protocol.ts +53 -0
- package/src/runtime/docker/ingress/build-config.ts +819 -0
- package/src/runtime/docker/ingress/index.ts +16 -0
- package/src/runtime/docker/ingress/types.ts +182 -0
- package/src/runtime/docker/log-frames.ts +121 -0
- package/src/runtime/docker/logs-server/index.ts +366 -0
- package/src/runtime/docker/spawn-server/context-key.ts +82 -0
- package/src/runtime/docker/spawn-server/index.ts +492 -0
- package/src/runtime/docker/spawn-server/protocol.ts +70 -0
- package/src/runtime/docker/spawn-server/spec.ts +122 -0
- package/src/runtime/docker/stats-server/disks.ts +161 -0
- package/src/runtime/docker/stats-server/index.ts +322 -0
- package/src/runtime/home-client/index.ts +71 -0
- package/src/runtime/log-line.ts +24 -0
- package/src/runtime/logs-client/index.ts +175 -0
- package/src/runtime/spawn-client/index.ts +93 -0
- package/src/runtime/spawn-client/kaniko.ts +79 -0
- package/src/runtime/stats-client/index.ts +65 -0
- package/src/runtime/stats-sample.ts +95 -0
- package/src/safe-json.ts +37 -0
- package/src/sandbox.ts +1209 -0
- package/src/schema/build-apps.ts +38 -0
- package/src/schema/home.ts +217 -0
- package/src/schema/host.ts +792 -0
- package/src/schema/inputs.ts +26 -0
- package/src/schema/nodes.ts +75 -0
- package/src/schema/peer.ts +400 -0
- package/src/schema/public-domain.ts +116 -0
- package/src/schema/remote.ts +74 -0
- package/src/security.ts +594 -0
- package/src/serialize.ts +21 -0
- package/src/service-definition.ts +284 -0
- package/src/service-url.ts +64 -0
- package/src/service.ts +39 -0
- package/src/services/index.ts +5 -0
- package/src/shared.ts +65 -0
- package/src/speculative-silo.ts +96 -0
- package/src/stable-secret.ts +14 -0
- package/src/surfaces.ts +264 -0
- package/src/tailwind-bin.ts +56 -0
- package/src/test/boot.ts +192 -0
- package/src/test/compile.ts +501 -0
- package/src/test/describe.ts +117 -0
- package/src/test/index.ts +11 -0
- package/src/test/ingress-fixture.ts +37 -0
- package/src/test/types.ts +318 -0
- package/src/trusted-headers.ts +79 -0
- package/src/types.ts +126 -0
- package/src/ui/components/aside.tsx +96 -0
- package/src/ui/components/badge.tsx +42 -0
- package/src/ui/components/button.tsx +59 -0
- package/src/ui/components/card.tsx +106 -0
- package/src/ui/components/checkbox.tsx +37 -0
- package/src/ui/components/code-block.tsx +29 -0
- package/src/ui/components/combobox.tsx +119 -0
- package/src/ui/components/config-field.tsx +284 -0
- package/src/ui/components/dialog.tsx +137 -0
- package/src/ui/components/domain-editor.tsx +317 -0
- package/src/ui/components/domain-list.ts +205 -0
- package/src/ui/components/dropdown-menu.tsx +73 -0
- package/src/ui/components/empty-state.tsx +51 -0
- package/src/ui/components/highlight.ts +59 -0
- package/src/ui/components/icons.tsx +101 -0
- package/src/ui/components/image-posture.tsx +85 -0
- package/src/ui/components/input.tsx +28 -0
- package/src/ui/components/label.tsx +16 -0
- package/src/ui/components/line-chart.tsx +113 -0
- package/src/ui/components/page.tsx +39 -0
- package/src/ui/components/permission-row.tsx +262 -0
- package/src/ui/components/permission-section.tsx +51 -0
- package/src/ui/components/pie-chart.tsx +245 -0
- package/src/ui/components/schema-block.tsx +60 -0
- package/src/ui/components/separator.tsx +42 -0
- package/src/ui/components/sheet.tsx +130 -0
- package/src/ui/components/sidebar.tsx +650 -0
- package/src/ui/components/silo-picker.tsx +510 -0
- package/src/ui/components/skeleton.tsx +13 -0
- package/src/ui/components/sonner.tsx +33 -0
- package/src/ui/components/switch.tsx +58 -0
- package/src/ui/components/table-view.ts +219 -0
- package/src/ui/components/table.tsx +506 -0
- package/src/ui/components/tabs.tsx +59 -0
- package/src/ui/components/timeago.tsx +84 -0
- package/src/ui/components/tooltip.tsx +56 -0
- package/src/ui/components.json +21 -0
- package/src/ui/dev/dev-shell.tsx +145 -0
- package/src/ui/home/home-grid.tsx +177 -0
- package/src/ui/hooks/use-mobile.ts +19 -0
- package/src/ui/index.ts +189 -0
- package/src/ui/install/adapter.ts +193 -0
- package/src/ui/install/deploy-targets-picker.tsx +185 -0
- package/src/ui/install/install-app-panel.tsx +912 -0
- package/src/ui/install/install-error.tsx +62 -0
- package/src/ui/install/perms-inactive.ts +50 -0
- package/src/ui/install/required-fields.ts +37 -0
- package/src/ui/install/tabs-form.tsx +557 -0
- package/src/ui/tokens.css +279 -0
- package/src/ui/utils.ts +78 -0
- package/src/ui-error.ts +139 -0
- package/src/ui-runtime/dev-landing-entry.tsx +258 -0
- package/src/ui-runtime/index.ts +83 -0
- package/src/ui-runtime/install-entry.tsx +119 -0
- package/src/ui-runtime/mount.ts +367 -0
- package/src/ui-runtime/runtime.css +56 -0
- package/src/ui-surface.ts +451 -0
- package/src/valkey.ts +43 -0
- package/src/worker/extract-tar.ts +125 -0
- package/src/worker/identity.ts +183 -0
- package/src/worker/index.ts +16 -0
- package/src/worker/public-ip.ts +92 -0
- package/src/worker/queue.ts +54 -0
- package/src/worker/reconcile.ts +127 -0
- package/src/worker/start-worker.ts +227 -0
- package/src/worker/ui-proxy.ts +335 -0
- package/src/worker/uplink.ts +523 -0
- package/src/worker/validate.ts +34 -0
- package/src/worker/verify-relay.ts +102 -0
- package/src/worker-protocol/app-dirs.ts +94 -0
- package/src/worker-protocol/home-proxy-paths.ts +76 -0
- package/src/worker-protocol/index.ts +55 -0
- package/src/worker-protocol/ingress.ts +166 -0
- package/src/worker-protocol/logs-socket-path.ts +50 -0
- package/src/worker-protocol/networks.ts +39 -0
- package/src/worker-protocol/node-version.ts +70 -0
- package/src/worker-protocol/silo-paths.ts +114 -0
- package/src/worker-protocol/spawn-paths.ts +165 -0
- package/src/worker-protocol/stats-socket-path.ts +40 -0
- package/src/worker-protocol/update-files.ts +77 -0
- package/src/worker-protocol/uplink/constants.ts +86 -0
- package/src/worker-protocol/uplink/errors.ts +61 -0
- package/src/worker-protocol/uplink/frame.ts +114 -0
- package/src/worker-protocol/uplink/identity.ts +40 -0
- package/src/worker-protocol/uplink/index.ts +63 -0
- package/src/worker-protocol/uplink/methods.ts +462 -0
- package/src/worker-protocol/uplink/mux.ts +646 -0
- package/src/worker-protocol/uplink/protocol.ts +11 -0
- package/src/worker-protocol/uplink/rpc.ts +309 -0
- package/src/worker-protocol/uplink/session.ts +540 -0
- package/src/worker-protocol/uplink/test-support.ts +188 -0
- package/src/worker-protocol/worker-mount.ts +27 -0
- package/src/worker-protocol/worker-state.ts +190 -0
- package/src/xml.ts +49 -0
- package/src/yaml.ts +15 -0
package/src/factory.ts
ADDED
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
// Factory-level wrapper composition: two seeds (perm/input), shared
|
|
2
|
+
// Mode-generic wrappers, two composed bases (basePermission, baseInput). Each
|
|
3
|
+
// wrapper owns one meta slot and refuses double-stacks via GateNoSlot.
|
|
4
|
+
|
|
5
|
+
import type { z } from "zod";
|
|
6
|
+
import type {
|
|
7
|
+
AddOptionalSlot,
|
|
8
|
+
AddRequiredSlot,
|
|
9
|
+
Builder,
|
|
10
|
+
FactoryFor,
|
|
11
|
+
GateNoSlot,
|
|
12
|
+
InputKind,
|
|
13
|
+
Mode,
|
|
14
|
+
} from "./shared.ts";
|
|
15
|
+
|
|
16
|
+
// Open template literal shape used as basePermission's KindUnion. The closed
|
|
17
|
+
// `PermissionKind` (derived from schema/* in permissions.ts) can't be
|
|
18
|
+
// referenced here without forming a type-level cycle: schema/host.ts →
|
|
19
|
+
// host-registry.ts → factory.ts → ... → typeof schema/host.ts. Every prefix a
|
|
20
|
+
// registry narrows to must appear here, or `make*(basePermission)` won't
|
|
21
|
+
// typecheck for that prefix.
|
|
22
|
+
type AnyPermissionKindShape =
|
|
23
|
+
| `host.${string}`
|
|
24
|
+
| `peers.${string}`
|
|
25
|
+
| `remote.${string}`
|
|
26
|
+
| `home.${string}`;
|
|
27
|
+
|
|
28
|
+
// ----- Runtime helpers -----------------------------------------------------
|
|
29
|
+
|
|
30
|
+
export function defineMethod<T extends object>(
|
|
31
|
+
target: T,
|
|
32
|
+
name: string,
|
|
33
|
+
fn: (this: T, ...args: never[]) => unknown,
|
|
34
|
+
): void {
|
|
35
|
+
Object.defineProperty(target, name, {
|
|
36
|
+
value: fn,
|
|
37
|
+
enumerable: false,
|
|
38
|
+
writable: false,
|
|
39
|
+
configurable: true,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function cloneAnd<T extends object>(source: T, patch: object): T {
|
|
44
|
+
const c = Object.create(
|
|
45
|
+
Object.getPrototypeOf(source),
|
|
46
|
+
Object.getOwnPropertyDescriptors(source),
|
|
47
|
+
) as T;
|
|
48
|
+
Object.assign(c, patch);
|
|
49
|
+
return c;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ----- Seeds ---------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
const makePermRecord: Builder<{ readonly meta: object }, "perm", AnyPermissionKindShape> = (<
|
|
55
|
+
S extends z.ZodType,
|
|
56
|
+
K extends AnyPermissionKindShape,
|
|
57
|
+
>(
|
|
58
|
+
kind: K,
|
|
59
|
+
schema: S,
|
|
60
|
+
) => {
|
|
61
|
+
return (input?: z.input<S>) => ({
|
|
62
|
+
kind,
|
|
63
|
+
data: schema.parse(input),
|
|
64
|
+
meta: {} as object,
|
|
65
|
+
});
|
|
66
|
+
}) as Builder<{ readonly meta: object }, "perm", AnyPermissionKindShape>;
|
|
67
|
+
|
|
68
|
+
const makeInputRecord: Builder<{ readonly meta: object }, "input", InputKind> = <
|
|
69
|
+
S extends z.ZodType,
|
|
70
|
+
K extends InputKind,
|
|
71
|
+
>(
|
|
72
|
+
kind: K,
|
|
73
|
+
schema: S,
|
|
74
|
+
) => {
|
|
75
|
+
return () => ({
|
|
76
|
+
kind,
|
|
77
|
+
schema,
|
|
78
|
+
meta: {} as object,
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// ----- Mode-agnostic wrappers ---------------------------------------------
|
|
83
|
+
|
|
84
|
+
interface WithOptional {
|
|
85
|
+
// The intersection narrows the existing `meta.isOptional: boolean` slot
|
|
86
|
+
// to the literal `true`, which lets type-level walkers (`GrantsOf`,
|
|
87
|
+
// `ConfigOf`'s optional-key handling) tell `r.x()` apart from
|
|
88
|
+
// `r.x().optional()`.
|
|
89
|
+
optional(this: this): this & { readonly meta: { readonly isOptional: true } };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function makeOptional<BaseR extends { meta: object }, M extends Mode, KindUnion extends string>(
|
|
93
|
+
inner: Builder<BaseR, M, KindUnion>,
|
|
94
|
+
..._gate: GateNoSlot<BaseR["meta"], "isOptional">
|
|
95
|
+
): Builder<
|
|
96
|
+
BaseR & {
|
|
97
|
+
readonly meta: AddRequiredSlot<BaseR["meta"], "isOptional", boolean>;
|
|
98
|
+
} & WithOptional,
|
|
99
|
+
M,
|
|
100
|
+
KindUnion
|
|
101
|
+
> {
|
|
102
|
+
return (<S extends z.ZodType, K extends KindUnion>(kind: K, schema: S) => {
|
|
103
|
+
const factory = inner<S, K>(kind, schema) as (...args: unknown[]) => { meta: object };
|
|
104
|
+
return ((...args: unknown[]) => {
|
|
105
|
+
const innerRecord = factory(...args);
|
|
106
|
+
// Use cloneAnd (not spread) so non-enumerable chain methods carry over.
|
|
107
|
+
const seeded = cloneAnd(innerRecord, {
|
|
108
|
+
meta: { ...(innerRecord.meta as object), isOptional: false },
|
|
109
|
+
});
|
|
110
|
+
defineMethod(seeded, "optional", function (this: typeof seeded) {
|
|
111
|
+
return cloneAnd(this, { meta: { ...(this.meta as object), isOptional: true } });
|
|
112
|
+
});
|
|
113
|
+
return seeded;
|
|
114
|
+
}) as unknown as FactoryFor<
|
|
115
|
+
S,
|
|
116
|
+
K,
|
|
117
|
+
BaseR & {
|
|
118
|
+
readonly meta: AddRequiredSlot<BaseR["meta"], "isOptional", boolean>;
|
|
119
|
+
} & WithOptional,
|
|
120
|
+
M
|
|
121
|
+
>;
|
|
122
|
+
}) as Builder<
|
|
123
|
+
BaseR & {
|
|
124
|
+
readonly meta: AddRequiredSlot<BaseR["meta"], "isOptional", boolean>;
|
|
125
|
+
} & WithOptional,
|
|
126
|
+
M,
|
|
127
|
+
KindUnion
|
|
128
|
+
>;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
interface WithDescribe {
|
|
132
|
+
describe(this: this, text: string): this;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function makeDescribable<BaseR extends { meta: object }, M extends Mode, KindUnion extends string>(
|
|
136
|
+
inner: Builder<BaseR, M, KindUnion>,
|
|
137
|
+
..._gate: GateNoSlot<BaseR["meta"], "description">
|
|
138
|
+
): Builder<
|
|
139
|
+
BaseR & {
|
|
140
|
+
readonly meta: AddOptionalSlot<BaseR["meta"], "description", string>;
|
|
141
|
+
} & WithDescribe,
|
|
142
|
+
M,
|
|
143
|
+
KindUnion
|
|
144
|
+
> {
|
|
145
|
+
return (<S extends z.ZodType, K extends KindUnion>(kind: K, schema: S) => {
|
|
146
|
+
const factory = inner<S, K>(kind, schema) as (...args: unknown[]) => { meta: object };
|
|
147
|
+
return ((...args: unknown[]) => {
|
|
148
|
+
const record = factory(...args);
|
|
149
|
+
defineMethod(record, "describe", function (this: typeof record, text: string) {
|
|
150
|
+
return cloneAnd(this, { meta: { ...(this.meta as object), description: text } });
|
|
151
|
+
});
|
|
152
|
+
return record;
|
|
153
|
+
}) as unknown as FactoryFor<
|
|
154
|
+
S,
|
|
155
|
+
K,
|
|
156
|
+
BaseR & {
|
|
157
|
+
readonly meta: AddOptionalSlot<BaseR["meta"], "description", string>;
|
|
158
|
+
} & WithDescribe,
|
|
159
|
+
M
|
|
160
|
+
>;
|
|
161
|
+
}) as Builder<
|
|
162
|
+
BaseR & {
|
|
163
|
+
readonly meta: AddOptionalSlot<BaseR["meta"], "description", string>;
|
|
164
|
+
} & WithDescribe,
|
|
165
|
+
M,
|
|
166
|
+
KindUnion
|
|
167
|
+
>;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ----- Permission-only wrappers -------------------------------------------
|
|
171
|
+
|
|
172
|
+
// `.reason()` widens meta.reason from optional to required, transitioning a
|
|
173
|
+
// Draft permission to Complete (assignable to the configSchema slot).
|
|
174
|
+
interface WithReason {
|
|
175
|
+
reason(this: this, text: string): this & { readonly meta: { readonly reason: string } };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// `.schema(z)` lands a live Zod schema as a non-enumerable `responseSchema`
|
|
179
|
+
// property — JSON.stringify drops it; the sandbox reads it back inside QuickJS.
|
|
180
|
+
// The method name (`schema`) and the property name (`responseSchema`) must
|
|
181
|
+
// differ; one identifier for both breaks polymorphic-`this` chaining for
|
|
182
|
+
// `.reason()` etc.
|
|
183
|
+
interface WithSchema {
|
|
184
|
+
schema<S extends z.ZodType>(this: this, s: S): this & { readonly responseSchema: S };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function makeReasonable<BaseR extends { meta: object }, KindUnion extends string>(
|
|
188
|
+
inner: Builder<BaseR, "perm", KindUnion>,
|
|
189
|
+
..._gate: GateNoSlot<BaseR["meta"], "reason">
|
|
190
|
+
): Builder<
|
|
191
|
+
BaseR & {
|
|
192
|
+
readonly meta: AddOptionalSlot<BaseR["meta"], "reason", string>;
|
|
193
|
+
} & WithReason,
|
|
194
|
+
"perm",
|
|
195
|
+
KindUnion
|
|
196
|
+
> {
|
|
197
|
+
return (<S extends z.ZodType, K extends KindUnion>(kind: K, schema: S) => {
|
|
198
|
+
const factory = inner<S, K>(kind, schema);
|
|
199
|
+
return ((input?: z.input<S>) => {
|
|
200
|
+
const record = (factory as unknown as (input?: z.input<S>) => { meta: object })(input);
|
|
201
|
+
defineMethod(record, "reason", function (this: typeof record, text: string) {
|
|
202
|
+
return cloneAnd(this, { meta: { ...(this.meta as object), reason: text } });
|
|
203
|
+
});
|
|
204
|
+
return record;
|
|
205
|
+
}) as unknown as FactoryFor<
|
|
206
|
+
S,
|
|
207
|
+
K,
|
|
208
|
+
BaseR & {
|
|
209
|
+
readonly meta: AddOptionalSlot<BaseR["meta"], "reason", string>;
|
|
210
|
+
} & WithReason,
|
|
211
|
+
"perm"
|
|
212
|
+
>;
|
|
213
|
+
}) as Builder<
|
|
214
|
+
BaseR & {
|
|
215
|
+
readonly meta: AddOptionalSlot<BaseR["meta"], "reason", string>;
|
|
216
|
+
} & WithReason,
|
|
217
|
+
"perm",
|
|
218
|
+
KindUnion
|
|
219
|
+
>;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Opt-in mixin for factories that carry a live Zod object (today:
|
|
223
|
+
// remote.fetchUrl).
|
|
224
|
+
function makeSchemable<BaseR extends object, KindUnion extends string>(
|
|
225
|
+
inner: Builder<BaseR, "perm", KindUnion>,
|
|
226
|
+
..._gate: GateNoSlot<BaseR, "responseSchema">
|
|
227
|
+
): Builder<BaseR & WithSchema, "perm", KindUnion> {
|
|
228
|
+
return (<S extends z.ZodType, K extends KindUnion>(kind: K, schema: S) => {
|
|
229
|
+
const factory = inner<S, K>(kind, schema);
|
|
230
|
+
return ((input?: z.input<S>) => {
|
|
231
|
+
const record = (factory as unknown as (input?: z.input<S>) => object)(input);
|
|
232
|
+
defineMethod(record, "schema", function (this: object, s: z.ZodType) {
|
|
233
|
+
const cloned = cloneAnd(this, {});
|
|
234
|
+
Object.defineProperty(cloned, "responseSchema", {
|
|
235
|
+
value: s,
|
|
236
|
+
enumerable: false,
|
|
237
|
+
writable: false,
|
|
238
|
+
configurable: true,
|
|
239
|
+
});
|
|
240
|
+
return cloned;
|
|
241
|
+
});
|
|
242
|
+
return record;
|
|
243
|
+
}) as unknown as FactoryFor<S, K, BaseR & WithSchema, "perm">;
|
|
244
|
+
}) as Builder<BaseR & WithSchema, "perm", KindUnion>;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ----- Input-only wrappers ------------------------------------------------
|
|
248
|
+
|
|
249
|
+
interface WithTitle {
|
|
250
|
+
title(this: this, text: string): this;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function makeTitleable<BaseR extends { meta: object }, KindUnion extends string>(
|
|
254
|
+
inner: Builder<BaseR, "input", KindUnion>,
|
|
255
|
+
..._gate: GateNoSlot<BaseR["meta"], "title">
|
|
256
|
+
): Builder<
|
|
257
|
+
BaseR & {
|
|
258
|
+
readonly meta: AddOptionalSlot<BaseR["meta"], "title", string>;
|
|
259
|
+
} & WithTitle,
|
|
260
|
+
"input",
|
|
261
|
+
KindUnion
|
|
262
|
+
> {
|
|
263
|
+
return (<S extends z.ZodType, K extends KindUnion>(kind: K, schema: S) => {
|
|
264
|
+
const factory = inner<S, K>(kind, schema);
|
|
265
|
+
return (() => {
|
|
266
|
+
const record = (factory as () => { meta: object })();
|
|
267
|
+
defineMethod(record, "title", function (this: typeof record, text: string) {
|
|
268
|
+
return cloneAnd(this, { meta: { ...(this.meta as object), title: text } });
|
|
269
|
+
});
|
|
270
|
+
return record;
|
|
271
|
+
}) as unknown as FactoryFor<
|
|
272
|
+
S,
|
|
273
|
+
K,
|
|
274
|
+
BaseR & {
|
|
275
|
+
readonly meta: AddOptionalSlot<BaseR["meta"], "title", string>;
|
|
276
|
+
} & WithTitle,
|
|
277
|
+
"input"
|
|
278
|
+
>;
|
|
279
|
+
}) as Builder<
|
|
280
|
+
BaseR & {
|
|
281
|
+
readonly meta: AddOptionalSlot<BaseR["meta"], "title", string>;
|
|
282
|
+
} & WithTitle,
|
|
283
|
+
"input",
|
|
284
|
+
KindUnion
|
|
285
|
+
>;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// `.prefill` infers I from `this.schema` so the value is constrained to the
|
|
289
|
+
// schema's I. Non-generic at the BaseR level so it sits cleanly alongside
|
|
290
|
+
// other mixins.
|
|
291
|
+
interface WithPrefill {
|
|
292
|
+
prefill<I>(
|
|
293
|
+
this: this & { readonly schema: z.ZodType<I> },
|
|
294
|
+
value: I,
|
|
295
|
+
): this & { readonly meta: { readonly prefill: I } };
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function makePrefillable<BaseR extends { meta: object }, KindUnion extends string>(
|
|
299
|
+
inner: Builder<BaseR, "input", KindUnion>,
|
|
300
|
+
..._gate: GateNoSlot<BaseR["meta"], "prefill">
|
|
301
|
+
): Builder<
|
|
302
|
+
BaseR & {
|
|
303
|
+
readonly meta: AddOptionalSlot<BaseR["meta"], "prefill", unknown>;
|
|
304
|
+
} & WithPrefill,
|
|
305
|
+
"input",
|
|
306
|
+
KindUnion
|
|
307
|
+
> {
|
|
308
|
+
return (<S extends z.ZodType, K extends KindUnion>(kind: K, schema: S) => {
|
|
309
|
+
const factory = inner<S, K>(kind, schema);
|
|
310
|
+
return (() => {
|
|
311
|
+
const record = (factory as () => { meta: object })();
|
|
312
|
+
defineMethod(record, "prefill", function (this: typeof record, value: unknown) {
|
|
313
|
+
return cloneAnd(this, { meta: { ...(this.meta as object), prefill: value } });
|
|
314
|
+
});
|
|
315
|
+
return record;
|
|
316
|
+
}) as unknown as FactoryFor<
|
|
317
|
+
S,
|
|
318
|
+
K,
|
|
319
|
+
BaseR & {
|
|
320
|
+
readonly meta: AddOptionalSlot<BaseR["meta"], "prefill", unknown>;
|
|
321
|
+
} & WithPrefill,
|
|
322
|
+
"input"
|
|
323
|
+
>;
|
|
324
|
+
}) as Builder<
|
|
325
|
+
BaseR & {
|
|
326
|
+
readonly meta: AddOptionalSlot<BaseR["meta"], "prefill", unknown>;
|
|
327
|
+
} & WithPrefill,
|
|
328
|
+
"input",
|
|
329
|
+
KindUnion
|
|
330
|
+
>;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
interface WithPlaceholder {
|
|
334
|
+
placeholder(this: this, text: string): this;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function makePlaceholdable<BaseR extends { meta: object }, KindUnion extends string>(
|
|
338
|
+
inner: Builder<BaseR, "input", KindUnion>,
|
|
339
|
+
..._gate: GateNoSlot<BaseR["meta"], "placeholder">
|
|
340
|
+
): Builder<
|
|
341
|
+
BaseR & {
|
|
342
|
+
readonly meta: AddOptionalSlot<BaseR["meta"], "placeholder", string>;
|
|
343
|
+
} & WithPlaceholder,
|
|
344
|
+
"input",
|
|
345
|
+
KindUnion
|
|
346
|
+
> {
|
|
347
|
+
return (<S extends z.ZodType, K extends KindUnion>(kind: K, schema: S) => {
|
|
348
|
+
const factory = inner<S, K>(kind, schema);
|
|
349
|
+
return (() => {
|
|
350
|
+
const record = (factory as () => { meta: object })();
|
|
351
|
+
defineMethod(record, "placeholder", function (this: typeof record, text: string) {
|
|
352
|
+
return cloneAnd(this, { meta: { ...(this.meta as object), placeholder: text } });
|
|
353
|
+
});
|
|
354
|
+
return record;
|
|
355
|
+
}) as unknown as FactoryFor<
|
|
356
|
+
S,
|
|
357
|
+
K,
|
|
358
|
+
BaseR & {
|
|
359
|
+
readonly meta: AddOptionalSlot<BaseR["meta"], "placeholder", string>;
|
|
360
|
+
} & WithPlaceholder,
|
|
361
|
+
"input"
|
|
362
|
+
>;
|
|
363
|
+
}) as Builder<
|
|
364
|
+
BaseR & {
|
|
365
|
+
readonly meta: AddOptionalSlot<BaseR["meta"], "placeholder", string>;
|
|
366
|
+
} & WithPlaceholder,
|
|
367
|
+
"input",
|
|
368
|
+
KindUnion
|
|
369
|
+
>;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// ----- Number-input-only wrapper ------------------------------------------
|
|
373
|
+
|
|
374
|
+
// The three numeric slots a number input can carry, stacked onto BaseR's meta.
|
|
375
|
+
type WithNumericMeta<M> = AddOptionalSlot<
|
|
376
|
+
AddOptionalSlot<AddOptionalSlot<M, "int", boolean>, "min", number>,
|
|
377
|
+
"max",
|
|
378
|
+
number
|
|
379
|
+
>;
|
|
380
|
+
|
|
381
|
+
// `.int()` / `.min(n)` / `.max(n)` for `r.input.number()`. Each records the
|
|
382
|
+
// bound in `meta` (so the schema walker surfaces it to the Home UI as an
|
|
383
|
+
// `<input>` min/max/step without poking Zod internals) and tightens the
|
|
384
|
+
// record's live Zod schema, so the constraint holds wherever it is parsed.
|
|
385
|
+
// Composed onto `baseInput` for the number kind only, so `r.input.text().min()`
|
|
386
|
+
// stays a type error.
|
|
387
|
+
interface WithNumericConstraints {
|
|
388
|
+
int(this: this): this & { readonly meta: { readonly int: true } };
|
|
389
|
+
min(this: this, value: number): this & { readonly meta: { readonly min: number } };
|
|
390
|
+
max(this: this, value: number): this & { readonly meta: { readonly max: number } };
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function makeNumericConstraints<BaseR extends { meta: object }, KindUnion extends string>(
|
|
394
|
+
inner: Builder<BaseR, "input", KindUnion>,
|
|
395
|
+
): Builder<
|
|
396
|
+
BaseR & { readonly meta: WithNumericMeta<BaseR["meta"]> } & WithNumericConstraints,
|
|
397
|
+
"input",
|
|
398
|
+
KindUnion
|
|
399
|
+
> {
|
|
400
|
+
return (<S extends z.ZodType, K extends KindUnion>(kind: K, schema: S) => {
|
|
401
|
+
const factory = inner<S, K>(kind, schema);
|
|
402
|
+
return (() => {
|
|
403
|
+
// numberInput is only ever composed onto z.number(), so the record's
|
|
404
|
+
// schema is a ZodNumber at runtime; route through unknown since the
|
|
405
|
+
// generic S doesn't structurally overlap ZodNumber.
|
|
406
|
+
const record = (factory as unknown as () => { meta: object; schema: z.ZodNumber })();
|
|
407
|
+
defineMethod(record, "int", function (this: typeof record) {
|
|
408
|
+
return cloneAnd(this, {
|
|
409
|
+
meta: { ...(this.meta as object), int: true },
|
|
410
|
+
schema: this.schema.int(),
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
defineMethod(record, "min", function (this: typeof record, value: number) {
|
|
414
|
+
return cloneAnd(this, {
|
|
415
|
+
meta: { ...(this.meta as object), min: value },
|
|
416
|
+
schema: this.schema.min(value),
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
defineMethod(record, "max", function (this: typeof record, value: number) {
|
|
420
|
+
return cloneAnd(this, {
|
|
421
|
+
meta: { ...(this.meta as object), max: value },
|
|
422
|
+
schema: this.schema.max(value),
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
return record;
|
|
426
|
+
}) as unknown as FactoryFor<
|
|
427
|
+
S,
|
|
428
|
+
K,
|
|
429
|
+
BaseR & { readonly meta: WithNumericMeta<BaseR["meta"]> } & WithNumericConstraints,
|
|
430
|
+
"input"
|
|
431
|
+
>;
|
|
432
|
+
}) as Builder<
|
|
433
|
+
BaseR & { readonly meta: WithNumericMeta<BaseR["meta"]> } & WithNumericConstraints,
|
|
434
|
+
"input",
|
|
435
|
+
KindUnion
|
|
436
|
+
>;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// ----- Composed bases ------------------------------------------------------
|
|
440
|
+
|
|
441
|
+
export const basePermission = makeOptional(makeDescribable(makeReasonable(makePermRecord)));
|
|
442
|
+
|
|
443
|
+
// Permission base with `.schema(z)` chained in. Use this for kinds that
|
|
444
|
+
// carry a live Zod object on the record (today: remote.fetchUrl).
|
|
445
|
+
// The chain method is mandatory at the type level — see CompleteOf.
|
|
446
|
+
export const schemablePermission = makeSchemable(basePermission);
|
|
447
|
+
|
|
448
|
+
export const baseInput = makeOptional(
|
|
449
|
+
makeDescribable(makeTitleable(makePrefillable(makePlaceholdable(makeInputRecord)))),
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
// `r.input.number()` adds the numeric-constraint chain on top of baseInput; the
|
|
453
|
+
// other input kinds use baseInput directly, so `.int()/.min()/.max()` only
|
|
454
|
+
// appear on number fields.
|
|
455
|
+
export const numberInput = makeNumericConstraints(baseInput);
|
|
456
|
+
|
|
457
|
+
// ----- Boundary contracts — derived from the compose chain -----------------
|
|
458
|
+
|
|
459
|
+
// `R extends WithSchema` means the factory was composed via `makeSchemable`,
|
|
460
|
+
// so a complete record must carry `responseSchema: ZodType` as a value. The
|
|
461
|
+
// else branch ({ responseSchema?: never }) excludes records that carry the
|
|
462
|
+
// property without going through the WithSchema chain.
|
|
463
|
+
export type CompleteOf<B> =
|
|
464
|
+
B extends Builder<infer R, infer M, infer K>
|
|
465
|
+
? M extends "perm"
|
|
466
|
+
? (R extends WithReason ? R & { readonly meta: { readonly reason: string } } : R) &
|
|
467
|
+
(R extends WithSchema
|
|
468
|
+
? { readonly responseSchema: z.ZodType<unknown> }
|
|
469
|
+
: { readonly responseSchema?: never }) & {
|
|
470
|
+
readonly kind: K;
|
|
471
|
+
readonly data: unknown;
|
|
472
|
+
}
|
|
473
|
+
: R & {
|
|
474
|
+
readonly kind: K;
|
|
475
|
+
readonly schema: z.ZodType<unknown>;
|
|
476
|
+
}
|
|
477
|
+
: never;
|
|
478
|
+
|
|
479
|
+
export type CompletePermission = CompleteOf<typeof basePermission>;
|
|
480
|
+
export type CompleteSchemablePermission = CompleteOf<typeof schemablePermission>;
|
|
481
|
+
export type CompleteInput = CompleteOf<typeof baseInput>;
|
package/src/garage.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Ref } from "./ref.ts";
|
|
2
|
+
import { interpolate as i } from "./ref.ts";
|
|
3
|
+
import { Service } from "./service.ts";
|
|
4
|
+
|
|
5
|
+
interface GarageOptions {
|
|
6
|
+
version?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class Garage extends Service {
|
|
10
|
+
readonly image: string;
|
|
11
|
+
readonly port = 3900;
|
|
12
|
+
readonly adminPort = 3903;
|
|
13
|
+
readonly bucket = "data";
|
|
14
|
+
|
|
15
|
+
constructor(options?: GarageOptions) {
|
|
16
|
+
super();
|
|
17
|
+
this.image = `dxflrs/garage:${options?.version ?? "v1"}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get accessKey(): Ref<string> {
|
|
21
|
+
return this.derived("access-key");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get secretKey(): Ref<string> {
|
|
25
|
+
return this.derived("secret-key");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get url(): Ref<string> {
|
|
29
|
+
return i`http://${this.host}:${this.port}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get adminUrl(): Ref<string> {
|
|
33
|
+
return i`http://${this.host}:${this.adminPort}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
getServiceDefinition() {
|
|
37
|
+
return {
|
|
38
|
+
image: this.image,
|
|
39
|
+
volumes: [
|
|
40
|
+
{
|
|
41
|
+
hostPath: i`./data/${this.host}`,
|
|
42
|
+
containerPath: "/var/lib/garage/data",
|
|
43
|
+
backup: { kind: "file-copy" as const },
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
env: {
|
|
47
|
+
GARAGE_ACCESS_KEY: this.accessKey,
|
|
48
|
+
GARAGE_SECRET_KEY: this.secretKey,
|
|
49
|
+
GARAGE_DEFAULT_BUCKET: this.bucket,
|
|
50
|
+
},
|
|
51
|
+
healthcheck: {
|
|
52
|
+
test: ["CMD", "/garage", "status"],
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|