@homeflare/alchemy 0.10.0 → 0.11.0
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/dist/caddy/index.js.map +2 -2
- package/dist/launchd/index.js.map +2 -2
- package/dist/openbao/index.js +10 -8
- package/dist/openbao/index.js.map +4 -4
- package/dist/ownership/adopting.d.ts +34 -0
- package/dist/ownership/adopting.d.ts.map +1 -0
- package/dist/ownership/probe.d.ts +15 -5
- package/dist/ownership/probe.d.ts.map +1 -1
- package/dist/ownership/rows.d.ts +12 -0
- package/dist/ownership/rows.d.ts.map +1 -1
- package/dist/ownership/store-fixture.d.ts +10 -0
- package/dist/ownership/store-fixture.d.ts.map +1 -0
- package/dist/proxmox/fake-pve-lxc.d.ts +8 -0
- package/dist/proxmox/fake-pve-lxc.d.ts.map +1 -1
- package/dist/proxmox/index.js +147 -113
- package/dist/proxmox/index.js.map +8 -6
- package/dist/proxmox/lxc-adoption.d.ts +32 -0
- package/dist/proxmox/lxc-adoption.d.ts.map +1 -0
- package/dist/proxmox/lxc-harness.d.ts +12 -0
- package/dist/proxmox/lxc-harness.d.ts.map +1 -1
- package/dist/proxmox/lxc.d.ts +5 -5
- package/dist/proxmox/lxc.d.ts.map +1 -1
- package/dist/verify/cli.js +79 -29
- package/dist/verify/cli.js.map +8 -6
- package/dist/verify/fake-engine.d.ts +9 -1
- package/dist/verify/fake-engine.d.ts.map +1 -1
- package/dist/verify/index.d.ts +1 -1
- package/dist/verify/index.d.ts.map +1 -1
- package/dist/verify/index.js +67 -17
- package/dist/verify/index.js.map +8 -6
- package/dist/verify/plan-scope.d.ts +14 -0
- package/dist/verify/plan-scope.d.ts.map +1 -0
- package/dist/verify/recheck.d.ts +42 -0
- package/dist/verify/recheck.d.ts.map +1 -0
- package/dist/verify/rows.d.ts +13 -3
- package/dist/verify/rows.d.ts.map +1 -1
- package/dist/verify/spy.d.ts +12 -0
- package/dist/verify/spy.d.ts.map +1 -1
- package/dist/verify/verify.d.ts.map +1 -1
- package/docs/adopt-verify.md +13 -2
- package/docs/adopted-deploys.md +4 -2
- package/docs/ownership.md +1 -1
- package/docs/proxmox-lxc-adopt.md +83 -0
- package/docs/proxmox-lxc.md +7 -45
- package/docs/proxmox.md +2 -0
- package/package.json +1 -1
- package/src/ownership/adopting.test.ts +63 -0
- package/src/ownership/adopting.ts +46 -0
- package/src/ownership/ownership.test.ts +1 -34
- package/src/ownership/probe.ts +27 -14
- package/src/ownership/rows.ts +8 -2
- package/src/ownership/store-fixture.ts +50 -0
- package/src/proxmox/ceph-pool-adopt.test.ts +32 -0
- package/src/proxmox/fake-pve-lxc.ts +13 -1
- package/src/proxmox/lxc-adopt.test.ts +13 -20
- package/src/proxmox/lxc-adoption.ts +50 -0
- package/src/proxmox/lxc-guards.test.ts +6 -13
- package/src/proxmox/lxc-harness.ts +25 -2
- package/src/proxmox/lxc-ownership.test.ts +19 -14
- package/src/proxmox/lxc-strict-adopt.test.ts +104 -0
- package/src/proxmox/lxc.ts +16 -12
- package/src/verify/fake-engine.ts +24 -0
- package/src/verify/index.ts +1 -0
- package/src/verify/plan-scope.ts +34 -0
- package/src/verify/recheck.test.ts +56 -0
- package/src/verify/recheck.ts +71 -0
- package/src/verify/rows.ts +46 -5
- package/src/verify/spy.ts +14 -3
- package/src/verify/verify.test.ts +22 -0
- package/src/verify/verify.ts +38 -25
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a `diff` or `reconcile` that holds attributes is looking at an ADOPTION — a live object
|
|
3
|
+
* that reached this stack's state by being FOUND, not by this resource's own create — so a family
|
|
4
|
+
* whose adoption must never change what it takes over (`Proxmox.Lxc`) can refuse any difference
|
|
5
|
+
* instead of writing it. adopt.ts and probe.ts decide WHETHER an object may be claimed; this says,
|
|
6
|
+
* once it is claimed, whether the claim is an adoption.
|
|
7
|
+
*
|
|
8
|
+
* ★ STATE ALREADY SAYS WHICH, AT BOTH HALVES OF A DEPLOY (alchemy 2.0.0-beta.79):
|
|
9
|
+
* · PLAN, A COLD ADOPTION. The probe mints an instance id no row can hold (Plan.ts
|
|
10
|
+
* `generateInstanceId()`), and `diff` then runs with that id and the attributes the probe
|
|
11
|
+
* read. So no row holds the id.
|
|
12
|
+
* · APPLY, THE SAME ADOPTION. Apply commits an `updating` row with `adopting: true` under that id
|
|
13
|
+
* BEFORE it calls reconcile, and the flag stays until the update commits (Apply.ts). An
|
|
14
|
+
* adoption whose apply was interrupted keeps it, so the next plan's `diff` sees it too.
|
|
15
|
+
* · A RECOVERED CREATE. A `creating` row with no attributes, whose live object the recovery read
|
|
16
|
+
* found. It is this resource's own create when the row proves it (probe.ts `provenOurs`, the
|
|
17
|
+
* same proof the read asked). Otherwise the plan went on only because `--adopt` said so — an
|
|
18
|
+
* adoption like any other.
|
|
19
|
+
* · Any other row with attributes is state vouching for the object: not an adoption.
|
|
20
|
+
* ⚠️ OUTSIDE A STACK — no Stack or State in context, or a store that errors (rows.ts) — no row
|
|
21
|
+
* holds anything, so every call is an adoption: the strict answer, never the permissive one. A
|
|
22
|
+
* family then refuses an ordinary update's write as if it were an adoption's. `alchemy deploy`
|
|
23
|
+
* and the Alchemist session provide both services.
|
|
24
|
+
*/
|
|
25
|
+
import * as Effect from 'effect/Effect';
|
|
26
|
+
import type { Owner } from './adopt.ts';
|
|
27
|
+
/**
|
|
28
|
+
* Whether `owner`'s call is an adoption (header). `settled` is the family's "live matches these
|
|
29
|
+
* props", asked only for a recovered create, with the props of the row that recorded it.
|
|
30
|
+
* ⛔ A CREATE IS NEVER ONE HERE. `output` undefined is reconcile's create path, which has its own
|
|
31
|
+
* rule for a live object it finds (adopt.ts `refuseTakeover`).
|
|
32
|
+
*/
|
|
33
|
+
export declare const adopting: <E, R>(owner: Owner, settled: (recorded: unknown) => Effect.Effect<boolean, E, R>) => Effect.Effect<boolean, never, R>;
|
|
34
|
+
//# sourceMappingURL=adopting.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adopting.d.ts","sourceRoot":"","sources":["../../src/ownership/adopting.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAIxC;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,CAAC,SACpB,KAAK,WACH,CAAC,QAAQ,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,KAC3D,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAO9B,CAAC"}
|
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
import * as Effect from 'effect/Effect';
|
|
2
2
|
import type { Owner } from './adopt.ts';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Whether the state store proves the live object this instance's own: a row records `ask.instanceId`
|
|
5
|
+
* with its whole declaration (whole.ts) AND `settled` — the family's own "live matches these props"
|
|
6
|
+
* — says so. The one proof ownership/ accepts for a live object state holds no attributes for; the
|
|
7
|
+
* recovery read (`ownedRead`) and an adoption check (adopting.ts) both ask it.
|
|
8
|
+
* ⛔ `settled` NEVER FAILS THE CALLER. The recovery read runs with the INTERRUPTED deploy's props, so
|
|
8
9
|
* a declaration that cannot be evaluated any more (a fragments directory since moved, a group
|
|
9
10
|
* name since renamed) would fail every later plan, the fix included. It reads as "not proven
|
|
10
|
-
* ours" instead
|
|
11
|
+
* ours" instead, with the reason logged.
|
|
12
|
+
*/
|
|
13
|
+
export declare const provenOurs: <E, R>(ask: {
|
|
14
|
+
readonly fqn: string;
|
|
15
|
+
readonly instanceId: string;
|
|
16
|
+
}, settled: Effect.Effect<boolean, E, R>) => Effect.Effect<boolean, never, R>;
|
|
17
|
+
/**
|
|
18
|
+
* A family's `read` answer. With attributes in state it is `found` as is. Without, it is the probe
|
|
19
|
+
* or the recovery read (header): `found` is ours only when `provenOurs` says so, else `Unowned`,
|
|
20
|
+
* which `--adopt` resolves.
|
|
11
21
|
*/
|
|
12
22
|
export declare const ownedRead: <A extends object, E, R>(ask: Owner, found: A | undefined, settled: Effect.Effect<boolean, E, R>) => Effect.Effect<A | undefined, never, R>;
|
|
13
23
|
//# sourceMappingURL=probe.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../../src/ownership/probe.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGxC
|
|
1
|
+
{"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../../src/ownership/probe.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGxC;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,EAAE,CAAC,OACxB;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAAE,WACjD,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,KACpC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAoB9B,CAAC;AAEL;;;;GAIG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,OACzC,KAAK,SACH,CAAC,GAAG,SAAS,WACX,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,KACpC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,EAAE,KAAK,EAAE,CAAC,CAIpC,CAAC"}
|
package/dist/ownership/rows.d.ts
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import * as Effect from 'effect/Effect';
|
|
2
|
+
/** One generation of a persisted row, as far as ownership/ reads it. */
|
|
3
|
+
export type Generation = {
|
|
4
|
+
readonly instanceId?: unknown;
|
|
5
|
+
readonly status?: unknown;
|
|
6
|
+
readonly props?: unknown;
|
|
7
|
+
readonly attr?: unknown;
|
|
8
|
+
readonly old?: unknown;
|
|
9
|
+
/** Set by Apply on the `updating` row of an adoption, until that update commits (Apply.ts). */
|
|
10
|
+
readonly adopting?: unknown;
|
|
11
|
+
};
|
|
12
|
+
/** The generation `instanceId` names — at the FQN, or at a former FQN `renamedFrom` names. */
|
|
13
|
+
export declare const recordedGeneration: (fqn: string, instanceId: string) => Effect.Effect<Generation | undefined>;
|
|
2
14
|
/**
|
|
3
15
|
* What the state store says of `instanceId` for this resource (at its FQN, or at a former FQN its
|
|
4
16
|
* `renamedFrom` names — the planner migrates that row before it recovers it):
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rows.d.ts","sourceRoot":"","sources":["../../src/ownership/rows.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"rows.d.ts","sourceRoot":"","sources":["../../src/ownership/rows.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAmBxC,wEAAwE;AACxE,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;IACvB,+FAA+F;IAC/F,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAaF,8FAA8F;AAC9F,eAAO,MAAM,kBAAkB,QACxB,MAAM,cACC,MAAM,KACjB,MAAM,CAAC,MAAM,CAAC,UAAU,GAAG,SAAS,CASnC,CAAC;AAEL;;;;;;;;;GASG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAEtD,eAAO,MAAM,gBAAgB,QAAS,MAAM,cAAc,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAKrF,CAAC;AAEL;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,QAAS,MAAM,cAAc,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAI7E,CAAC;AAEJ;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,QAAS,MAAM,cAAc,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAUpF,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ResourceState } from 'alchemy/State/ResourceState';
|
|
2
|
+
import * as Effect from 'effect/Effect';
|
|
3
|
+
/** A row with no attributes: `creating`, or `replacing` when it carries an `old` generation. */
|
|
4
|
+
export declare const row: (fqn: string, instanceId: string, old?: unknown, props?: unknown) => ResourceState;
|
|
5
|
+
/**
|
|
6
|
+
* A stack `s` at stage `test` whose store holds `rows`: `A` and `B` declared with a `name`, and `Z`
|
|
7
|
+
* declared `renamedFrom('X')`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const withStore: <A>(rows: Record<string, ResourceState>, effect: Effect.Effect<A>) => Promise<A>;
|
|
10
|
+
//# sourceMappingURL=store-fixture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-fixture.d.ts","sourceRoot":"","sources":["../../src/ownership/store-fixture.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAGjE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,gGAAgG;AAChG,eAAO,MAAM,GAAG,QACT,MAAM,cACC,MAAM,QACZ,OAAO,UACN,OAAO,KACb,aAO6B,CAAC;AAEjC;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,QACnB,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,UAC3B,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KACvB,OAAO,CAAC,CAAC,CAgBT,CAAC"}
|
|
@@ -22,6 +22,14 @@ export type FakePve = {
|
|
|
22
22
|
* keyed `node/vmid` — a guest that appears between a plan and its deploy.
|
|
23
23
|
*/
|
|
24
24
|
readonly vanish: Map<string, number>;
|
|
25
|
+
/**
|
|
26
|
+
* Hand edits that land mid-deploy, keyed `node/vmid`: once that guest's config has been read
|
|
27
|
+
* `after` more times, `set` is merged into it — a change made between a plan and its apply.
|
|
28
|
+
*/
|
|
29
|
+
readonly edits: Map<string, {
|
|
30
|
+
after: number;
|
|
31
|
+
readonly set: Record<string, unknown>;
|
|
32
|
+
}>;
|
|
25
33
|
/** Config keys a `PUT …/config` accepts with 200 and then does not store. */
|
|
26
34
|
readonly ignore: Set<string>;
|
|
27
35
|
/** How every task ends, and whether a task-starting call answers with no UPID at all. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fake-pve-lxc.d.ts","sourceRoot":"","sources":["../../src/proxmox/fake-pve-lxc.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,IAAI,GAAG;IACjB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,6FAA6F;IAC7F,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,mFAAmF;IACnF,QAAQ,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACzE,4FAA4F;IAC5F,QAAQ,CAAC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACtE;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,yFAAyF;IACzF,QAAQ,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC;IACjD,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC;CAC3B,CAAC;AAEF,+FAA+F;AAC/F,eAAO,MAAM,WAAW,eAAe,CAAC;AACxC,eAAO,MAAM,QAAQ,yBAAyB,CAAC;AAwC/C,eAAO,MAAM,OAAO,QAAO,
|
|
1
|
+
{"version":3,"file":"fake-pve-lxc.d.ts","sourceRoot":"","sources":["../../src/proxmox/fake-pve-lxc.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,IAAI,GAAG;IACjB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,6FAA6F;IAC7F,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,mFAAmF;IACnF,QAAQ,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACzE,4FAA4F;IAC5F,QAAQ,CAAC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACtE;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IACtF,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,yFAAyF;IACzF,QAAQ,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC;IACjD,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC;CAC3B,CAAC;AAEF,+FAA+F;AAC/F,eAAO,MAAM,WAAW,eAAe,CAAC;AACxC,eAAO,MAAM,QAAQ,yBAAyB,CAAC;AAwC/C,eAAO,MAAM,OAAO,QAAO,OA2H1B,CAAC;AAEF,8FAA8F;AAC9F,eAAO,MAAM,QAAQ,SAAU,OAAO,aAG0C,CAAC"}
|