@owlmeans/viable-common 0.0.33 → 0.0.35
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/README.md +3 -3
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/agent-presentation/SKILL.md +1 -1
- package/agent-meta/skills/viable-common/SKILL.md +93 -62
- package/build/ba/index.d.ts +0 -1
- package/build/ba/index.d.ts.map +1 -1
- package/build/ba/index.js +0 -1
- package/build/ba/index.js.map +1 -1
- package/build/blueprint/consts.d.ts +38 -0
- package/build/blueprint/consts.d.ts.map +1 -1
- package/build/blueprint/consts.js +40 -0
- package/build/blueprint/consts.js.map +1 -1
- package/build/blueprint/index.d.ts +0 -3
- package/build/blueprint/index.d.ts.map +1 -1
- package/build/blueprint/index.js +0 -3
- package/build/blueprint/index.js.map +1 -1
- package/build/blueprint/types.d.ts +5 -5
- package/build/intent/consts.d.ts +57 -0
- package/build/intent/consts.d.ts.map +1 -0
- package/build/intent/consts.js +58 -0
- package/build/intent/consts.js.map +1 -0
- package/build/intent/entrypoints.d.ts +27 -0
- package/build/intent/entrypoints.d.ts.map +1 -0
- package/build/intent/entrypoints.js +28 -0
- package/build/intent/entrypoints.js.map +1 -0
- package/build/intent/errors.d.ts +20 -0
- package/build/intent/errors.d.ts.map +1 -0
- package/build/intent/errors.js +28 -0
- package/build/intent/errors.js.map +1 -0
- package/build/intent/flow.d.ts +17 -0
- package/build/intent/flow.d.ts.map +1 -0
- package/build/intent/flow.js +65 -0
- package/build/intent/flow.js.map +1 -0
- package/build/intent/index.d.ts +7 -0
- package/build/intent/index.d.ts.map +1 -0
- package/build/intent/index.js +6 -0
- package/build/intent/index.js.map +1 -0
- package/build/intent/schemas.d.ts +10 -0
- package/build/intent/schemas.d.ts.map +1 -0
- package/build/intent/schemas.js +24 -0
- package/build/intent/schemas.js.map +1 -0
- package/build/intent/types.d.ts +40 -0
- package/build/intent/types.d.ts.map +1 -0
- package/build/intent/types.js +2 -0
- package/build/intent/types.js.map +1 -0
- package/build/scaffold/consts.d.ts +14 -1
- package/build/scaffold/consts.d.ts.map +1 -1
- package/build/scaffold/consts.js +10 -0
- package/build/scaffold/consts.js.map +1 -1
- package/build/scaffold/schemas.d.ts +7 -0
- package/build/scaffold/schemas.d.ts.map +1 -1
- package/build/scaffold/schemas.js +119 -68
- package/build/scaffold/schemas.js.map +1 -1
- package/build/scaffold/types.d.ts +62 -24
- package/build/scaffold/types.d.ts.map +1 -1
- package/build/skills/consts.d.ts +0 -34
- package/build/skills/consts.d.ts.map +1 -1
- package/build/skills/consts.js +0 -106
- package/build/skills/consts.js.map +1 -1
- package/build/skills/index.d.ts +0 -1
- package/build/skills/index.d.ts.map +1 -1
- package/build/skills/index.js +0 -1
- package/build/skills/index.js.map +1 -1
- package/build/skills/roles.d.ts +0 -9
- package/build/skills/roles.d.ts.map +1 -1
- package/build/skills/roles.js +0 -226
- package/build/skills/roles.js.map +1 -1
- package/build/ux/schemas.js +2 -2
- package/build/ux/schemas.js.map +1 -1
- package/package.json +17 -7
- package/src/ba/index.ts +0 -1
- package/src/blueprint/consts.ts +40 -0
- package/src/blueprint/index.ts +0 -3
- package/src/blueprint/types.ts +5 -5
- package/src/intent/consts.ts +66 -0
- package/src/intent/entrypoints.ts +43 -0
- package/src/intent/errors.ts +33 -0
- package/src/intent/flow.ts +67 -0
- package/src/intent/index.ts +6 -0
- package/src/intent/schemas.ts +27 -0
- package/src/intent/types.ts +45 -0
- package/src/scaffold/consts.ts +14 -1
- package/src/scaffold/schemas.ts +125 -69
- package/src/scaffold/types.ts +64 -23
- package/src/skills/consts.ts +0 -112
- package/src/skills/index.ts +0 -1
- package/src/skills/roles.ts +0 -267
- package/src/ux/schemas.ts +2 -2
- package/tests/blueprint.spec.ts +2 -66
- package/tests/intent.spec.ts +121 -0
- package/tests/scaffold.spec.ts +73 -13
- package/src/ba/schemas.ts +0 -196
- package/src/blueprint/case.ts +0 -320
- package/src/blueprint/resolve.ts +0 -136
- package/src/blueprint/schemas.ts +0 -44
- package/src/skills/catalogue.ts +0 -1864
- package/tests/skills.spec.ts +0 -125
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { contract, openProtocol, protocol, typed } from '@owlmeans/entrypoint';
|
|
2
|
+
import { frontend, route, RouteMethod } from '@owlmeans/route';
|
|
3
|
+
import { INTENT_API_PATH, INTENT_LANDING_PATH, intent } from './consts.js';
|
|
4
|
+
import { IntentPickupBodySchema, IntentStashBodySchema } from './schemas.js';
|
|
5
|
+
/**
|
|
6
|
+
* Declare the intent-first hand-off: two GUEST API routes and the landing screen's address.
|
|
7
|
+
*
|
|
8
|
+
* No guard and no gate anywhere on the API — the caller is a visitor who has no account yet — and
|
|
9
|
+
* no `service` on any route: each consumer fills it in (the public site names the platform's
|
|
10
|
+
* services in its own configuration; the manager binds them to its own).
|
|
11
|
+
*
|
|
12
|
+
* TODO(cors): these routes ride the framework's global `origin: '*'` CORS policy (no credentials).
|
|
13
|
+
* If viable ever restricts allowed origins globally, this entrypoint must keep answering the public
|
|
14
|
+
* site's origins — owlmeans.com, owlmeans.pl and, in dev/stage, `http://localhost:4321`.
|
|
15
|
+
*
|
|
16
|
+
* The landing screen is `sticky`, like the OAuth consent screens: it is an ordinary in-app screen
|
|
17
|
+
* that a visitor arrives at directly from another origin.
|
|
18
|
+
*/
|
|
19
|
+
export const makeIntentProtocols = (opts = {}) => {
|
|
20
|
+
const base = openProtocol(route(intent.base, opts.path ?? INTENT_API_PATH));
|
|
21
|
+
return {
|
|
22
|
+
base,
|
|
23
|
+
stash: protocol(route(intent.stash, '/', { parent: base, method: RouteMethod.POST }), contract.request({ body: typed(IntentStashBodySchema) }, typed())),
|
|
24
|
+
pickup: protocol(route(intent.pickup, '/pickup', { parent: base, method: RouteMethod.POST }), contract.request({ body: typed(IntentPickupBodySchema) }, typed())),
|
|
25
|
+
landing: openProtocol(route(intent.landing, opts.landing ?? INTENT_LANDING_PATH, frontend()), { sticky: true }),
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=entrypoints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entrypoints.js","sourceRoot":"","sources":["../../src/intent/entrypoints.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAA;AAC9E,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC9D,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC1E,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAK5E;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,IAAI,GAA0B,EAAE,EAAE,EAAE;IACtE,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,eAAe,CAAC,CAAC,CAAA;IAE3E,OAAO;QACL,IAAI;QAEJ,KAAK,EAAE,QAAQ,CACb,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC,EACpE,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,CAAkB,qBAAqB,CAAC,EAAE,EAAE,KAAK,EAAqB,CAAC,CACtG;QAED,MAAM,EAAE,QAAQ,CACd,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC,EAC3E,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,CAAmB,sBAAsB,CAAC,EAAE,EAAE,KAAK,EAAsB,CAAC,CACzG;QAED,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,mBAAmB,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;KAChH,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ResilientError } from '@owlmeans/error';
|
|
2
|
+
export declare class IntentError extends ResilientError {
|
|
3
|
+
static typeName: string;
|
|
4
|
+
constructor(message?: string);
|
|
5
|
+
}
|
|
6
|
+
/** No stashed prompt under that reference — expired, already picked up, or never existed. */
|
|
7
|
+
export declare class IntentExpired extends IntentError {
|
|
8
|
+
static typeName: string;
|
|
9
|
+
/** The reference answers nothing (any more): answered 404. */
|
|
10
|
+
static httpStatus: number;
|
|
11
|
+
constructor(message?: string);
|
|
12
|
+
}
|
|
13
|
+
/** Too many stashes or pickups from one address, or across everyone. */
|
|
14
|
+
export declare class IntentThrottled extends IntentError {
|
|
15
|
+
static typeName: string;
|
|
16
|
+
/** Answered 429. */
|
|
17
|
+
static httpStatus: number;
|
|
18
|
+
constructor(message?: string);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/intent/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,qBAAa,WAAY,SAAQ,cAAc;IAC7C,OAAuB,QAAQ,SAA2C;IAE1E,YAAY,OAAO,GAAE,MAAgB,EAEpC;CACF;AAED,6FAA6F;AAC7F,qBAAa,aAAc,SAAQ,WAAW;IAC5C,OAAuB,QAAQ,SAAmC;IAClE,8DAA8D;IAC9D,OAAc,UAAU,SAAM;IAE9B,YAAY,OAAO,GAAE,MAAgB,EAGpC;CACF;AAED,wEAAwE;AACxE,qBAAa,eAAgB,SAAQ,WAAW;IAC9C,OAAuB,QAAQ,SAAqC;IACpE,oBAAoB;IACpB,OAAc,UAAU,SAAM;IAE9B,YAAY,OAAO,GAAE,MAAgB,EAGpC;CACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ResilientError } from '@owlmeans/error';
|
|
2
|
+
export class IntentError extends ResilientError {
|
|
3
|
+
static typeName = `ViableIntent${ResilientError.typeName}`;
|
|
4
|
+
constructor(message = 'error') {
|
|
5
|
+
super(IntentError.typeName, `viable-intent:${message}`);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
/** No stashed prompt under that reference — expired, already picked up, or never existed. */
|
|
9
|
+
export class IntentExpired extends IntentError {
|
|
10
|
+
static typeName = `Expired${IntentError.typeName}`;
|
|
11
|
+
/** The reference answers nothing (any more): answered 404. */
|
|
12
|
+
static httpStatus = 404;
|
|
13
|
+
constructor(message = 'error') {
|
|
14
|
+
super(`expired:${message}`);
|
|
15
|
+
this.type = IntentExpired.typeName;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** Too many stashes or pickups from one address, or across everyone. */
|
|
19
|
+
export class IntentThrottled extends IntentError {
|
|
20
|
+
static typeName = `Throttled${IntentError.typeName}`;
|
|
21
|
+
/** Answered 429. */
|
|
22
|
+
static httpStatus = 429;
|
|
23
|
+
constructor(message = 'error') {
|
|
24
|
+
super(`throttled:${message}`);
|
|
25
|
+
this.type = IntentThrottled.typeName;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/intent/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,MAAM,OAAO,WAAY,SAAQ,cAAc;IACtC,MAAM,CAAU,QAAQ,GAAG,eAAe,cAAc,CAAC,QAAQ,EAAE,CAAA;IAE1E,YAAY,OAAO,GAAW,OAAO;QACnC,KAAK,CAAC,WAAW,CAAC,QAAQ,EAAE,iBAAiB,OAAO,EAAE,CAAC,CAAA;IACzD,CAAC;CACF;AAED,6FAA6F;AAC7F,MAAM,OAAO,aAAc,SAAQ,WAAW;IACrC,MAAM,CAAU,QAAQ,GAAG,UAAU,WAAW,CAAC,QAAQ,EAAE,CAAA;IAClE,8DAA8D;IACvD,MAAM,CAAC,UAAU,GAAG,GAAG,CAAA;IAE9B,YAAY,OAAO,GAAW,OAAO;QACnC,KAAK,CAAC,WAAW,OAAO,EAAE,CAAC,CAAA;QAC3B,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC,QAAQ,CAAA;IACpC,CAAC;CACF;AAED,wEAAwE;AACxE,MAAM,OAAO,eAAgB,SAAQ,WAAW;IACvC,MAAM,CAAU,QAAQ,GAAG,YAAY,WAAW,CAAC,QAAQ,EAAE,CAAA;IACpE,oBAAoB;IACb,MAAM,CAAC,UAAU,GAAG,GAAG,CAAA;IAE9B,YAAY,OAAO,GAAW,OAAO;QACnC,KAAK,CAAC,aAAa,OAAO,EAAE,CAAC,CAAA;QAC7B,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC,QAAQ,CAAA;IACtC,CAAC;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ShallowFlow } from '@owlmeans/flow';
|
|
2
|
+
/**
|
|
3
|
+
* The hand-off flow both sides walk.
|
|
4
|
+
*
|
|
5
|
+
* `compose` runs on the public site: the prompt is stashed and `handoff` carries the reference to
|
|
6
|
+
* `land`, whose module is the platform's landing screen — so the site redirects to
|
|
7
|
+
* `land`'s address with the payload as query. The landing then enters at `land` FRESH (the
|
|
8
|
+
* reference arrives as `?ref=`, never as a serialized token, which the platform's own sign-in
|
|
9
|
+
* parses from `?flow=` on every page), and either goes on to `review` (signed in) or parks the flow
|
|
10
|
+
* with `suspendFlow` at `sign-in`, whose non-explicit way forward is `review` — the home screen —
|
|
11
|
+
* so whichever sign-in method finishes lands there.
|
|
12
|
+
*
|
|
13
|
+
* The steps address CONCRETE entrypoint aliases, never `$`-prefixed references: this flow is never
|
|
14
|
+
* the live `FlowService` model and nothing here needs `configureFlows`.
|
|
15
|
+
*/
|
|
16
|
+
export declare const intentFlow: ShallowFlow;
|
|
17
|
+
//# sourceMappingURL=flow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow.d.ts","sourceRoot":"","sources":["../../src/intent/flow.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAKjD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,UAAU,EAAE,WA6CxB,CAAA"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { DISPATCHER } from '@owlmeans/auth';
|
|
2
|
+
import { HOME } from '@owlmeans/context';
|
|
3
|
+
import { INTENT_FLOW, INTENT_PAYLOAD_REF, intent, IntentFlowStep } from './consts.js';
|
|
4
|
+
const payloadMap = { [INTENT_PAYLOAD_REF]: 0 };
|
|
5
|
+
/**
|
|
6
|
+
* The hand-off flow both sides walk.
|
|
7
|
+
*
|
|
8
|
+
* `compose` runs on the public site: the prompt is stashed and `handoff` carries the reference to
|
|
9
|
+
* `land`, whose module is the platform's landing screen — so the site redirects to
|
|
10
|
+
* `land`'s address with the payload as query. The landing then enters at `land` FRESH (the
|
|
11
|
+
* reference arrives as `?ref=`, never as a serialized token, which the platform's own sign-in
|
|
12
|
+
* parses from `?flow=` on every page), and either goes on to `review` (signed in) or parks the flow
|
|
13
|
+
* with `suspendFlow` at `sign-in`, whose non-explicit way forward is `review` — the home screen —
|
|
14
|
+
* so whichever sign-in method finishes lands there.
|
|
15
|
+
*
|
|
16
|
+
* The steps address CONCRETE entrypoint aliases, never `$`-prefixed references: this flow is never
|
|
17
|
+
* the live `FlowService` model and nothing here needs `configureFlows`.
|
|
18
|
+
*/
|
|
19
|
+
export const intentFlow = {
|
|
20
|
+
flow: INTENT_FLOW,
|
|
21
|
+
initialStep: IntentFlowStep.Compose,
|
|
22
|
+
steps: {
|
|
23
|
+
[IntentFlowStep.Compose]: {
|
|
24
|
+
index: 0,
|
|
25
|
+
step: IntentFlowStep.Compose,
|
|
26
|
+
service: '',
|
|
27
|
+
initial: true,
|
|
28
|
+
payloadMap,
|
|
29
|
+
transitions: {
|
|
30
|
+
handoff: { transition: 'handoff', step: IntentFlowStep.Land },
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
[IntentFlowStep.Land]: {
|
|
34
|
+
index: 1,
|
|
35
|
+
step: IntentFlowStep.Land,
|
|
36
|
+
service: '',
|
|
37
|
+
module: intent.landing,
|
|
38
|
+
initial: true,
|
|
39
|
+
payloadMap,
|
|
40
|
+
transitions: {
|
|
41
|
+
review: { transition: 'review', step: IntentFlowStep.Review },
|
|
42
|
+
'sign-in': { transition: 'sign-in', step: IntentFlowStep.SignIn, explicit: true },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
[IntentFlowStep.SignIn]: {
|
|
46
|
+
index: 2,
|
|
47
|
+
step: IntentFlowStep.SignIn,
|
|
48
|
+
service: '',
|
|
49
|
+
module: DISPATCHER,
|
|
50
|
+
payloadMap,
|
|
51
|
+
transitions: {
|
|
52
|
+
next: { transition: 'next', step: IntentFlowStep.Review },
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
[IntentFlowStep.Review]: {
|
|
56
|
+
index: 3,
|
|
57
|
+
step: IntentFlowStep.Review,
|
|
58
|
+
service: '',
|
|
59
|
+
module: HOME,
|
|
60
|
+
payloadMap,
|
|
61
|
+
transitions: {},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=flow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow.js","sourceRoot":"","sources":["../../src/intent/flow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAA;AAExC,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAErF,MAAM,UAAU,GAAG,EAAE,CAAC,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAA;AAE9C;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,UAAU,GAAgB;IACrC,IAAI,EAAE,WAAW;IACjB,WAAW,EAAE,cAAc,CAAC,OAAO;IACnC,KAAK,EAAE;QACL,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE;YACxB,KAAK,EAAE,CAAC;YACR,IAAI,EAAE,cAAc,CAAC,OAAO;YAC5B,OAAO,EAAE,EAAE;YACX,OAAO,EAAE,IAAI;YACb,UAAU;YACV,WAAW,EAAE;gBACX,OAAO,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,IAAI,EAAE;aAC9D;SACF;QACD,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE;YACrB,KAAK,EAAE,CAAC;YACR,IAAI,EAAE,cAAc,CAAC,IAAI;YACzB,OAAO,EAAE,EAAE;YACX,MAAM,EAAE,MAAM,CAAC,OAAO;YACtB,OAAO,EAAE,IAAI;YACb,UAAU;YACV,WAAW,EAAE;gBACX,MAAM,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,CAAC,MAAM,EAAE;gBAC7D,SAAS,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;aAClF;SACF;QACD,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE;YACvB,KAAK,EAAE,CAAC;YACR,IAAI,EAAE,cAAc,CAAC,MAAM;YAC3B,OAAO,EAAE,EAAE;YACX,MAAM,EAAE,UAAU;YAClB,UAAU;YACV,WAAW,EAAE;gBACX,IAAI,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,CAAC,MAAM,EAAE;aAC1D;SACF;QACD,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE;YACvB,KAAK,EAAE,CAAC;YACR,IAAI,EAAE,cAAc,CAAC,MAAM;YAC3B,OAAO,EAAE,EAAE;YACX,MAAM,EAAE,IAAI;YACZ,UAAU;YACV,WAAW,EAAE,EAAE;SAChB;KACF;CACF,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/intent/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,kBAAkB,CAAA;AAChC,cAAc,WAAW,CAAA;AACzB,mBAAmB,YAAY,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/intent/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,kBAAkB,CAAA;AAChC,cAAc,WAAW,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { JSONSchemaType } from 'ajv';
|
|
2
|
+
import type { IntentPickupBody, IntentStashBody } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The stash body. `consent` is a constant `true`, not a boolean: the data-processing confirmation is
|
|
5
|
+
* a precondition of the request existing, so the wire refuses its absence instead of the handler
|
|
6
|
+
* having to remember to look.
|
|
7
|
+
*/
|
|
8
|
+
export declare const IntentStashBodySchema: JSONSchemaType<IntentStashBody>;
|
|
9
|
+
export declare const IntentPickupBodySchema: JSONSchemaType<IntentPickupBody>;
|
|
10
|
+
//# sourceMappingURL=schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/intent/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAA;AAEzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEnE;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAAC,eAAe,CAQjE,CAAA;AAED,eAAO,MAAM,sBAAsB,EAAE,cAAc,CAAC,gBAAgB,CAOnE,CAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { INTENT_PROMPT_MAX, INTENT_REF_LENGTH, INTENT_REF_PATTERN } from './consts.js';
|
|
2
|
+
/**
|
|
3
|
+
* The stash body. `consent` is a constant `true`, not a boolean: the data-processing confirmation is
|
|
4
|
+
* a precondition of the request existing, so the wire refuses its absence instead of the handler
|
|
5
|
+
* having to remember to look.
|
|
6
|
+
*/
|
|
7
|
+
export const IntentStashBodySchema = {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
prompt: { type: 'string', minLength: 1, maxLength: INTENT_PROMPT_MAX },
|
|
11
|
+
consent: { type: 'boolean', const: true },
|
|
12
|
+
},
|
|
13
|
+
required: ['prompt', 'consent'],
|
|
14
|
+
additionalProperties: false,
|
|
15
|
+
};
|
|
16
|
+
export const IntentPickupBodySchema = {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
ref: { type: 'string', minLength: INTENT_REF_LENGTH, maxLength: INTENT_REF_LENGTH, pattern: INTENT_REF_PATTERN },
|
|
20
|
+
},
|
|
21
|
+
required: ['ref'],
|
|
22
|
+
additionalProperties: false,
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../src/intent/schemas.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAGtF;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAoC;IACpE,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACV,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,iBAAiB,EAAE;QACtE,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE;KAC1C;IACD,QAAQ,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC/B,oBAAoB,EAAE,KAAK;CAC5B,CAAA;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAqC;IACtE,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACV,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,iBAAiB,EAAE,SAAS,EAAE,iBAAiB,EAAE,OAAO,EAAE,kBAAkB,EAAE;KACjH;IACD,QAAQ,EAAE,CAAC,KAAK,CAAC;IACjB,oBAAoB,EAAE,KAAK;CAC5B,CAAA"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ResourceRecord } from '@owlmeans/resource';
|
|
2
|
+
/** What the public site sends: the prompt and the person's confirmation of its processing. */
|
|
3
|
+
export interface IntentStashBody {
|
|
4
|
+
prompt: string;
|
|
5
|
+
/** Always `true` — the site's checkbox; a request without it is refused by the schema. */
|
|
6
|
+
consent: true;
|
|
7
|
+
}
|
|
8
|
+
/** The reference the platform answers with, and the moment its record expires. */
|
|
9
|
+
export interface IntentStashResult {
|
|
10
|
+
ref: string;
|
|
11
|
+
/** Epoch milliseconds. */
|
|
12
|
+
expiresAt: number;
|
|
13
|
+
}
|
|
14
|
+
export interface IntentPickupBody {
|
|
15
|
+
ref: string;
|
|
16
|
+
}
|
|
17
|
+
export interface IntentPickupResult {
|
|
18
|
+
prompt: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The prompt as it waits in the visitor's browser between pickup and the decision on the home
|
|
22
|
+
* screen. `expiresAt` is checked on read — browser storage has no TTL of its own.
|
|
23
|
+
*/
|
|
24
|
+
export interface IntentDraft extends ResourceRecord {
|
|
25
|
+
id: string;
|
|
26
|
+
/** The reference it was picked up with; a reload of the landing reuses the draft of that ref. */
|
|
27
|
+
ref: string;
|
|
28
|
+
prompt: string;
|
|
29
|
+
expiresAt: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Options of {@link makeIntentProtocols}.
|
|
33
|
+
*/
|
|
34
|
+
export interface IntentProtocolOptions {
|
|
35
|
+
/** Path prefix of the guest API; defaults to `/public/intent`. */
|
|
36
|
+
path?: string;
|
|
37
|
+
/** Path of the landing screen; defaults to `/start`. */
|
|
38
|
+
landing?: string;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/intent/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAExD,8FAA8F;AAC9F,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,0FAA0F;IAC1F,OAAO,EAAE,IAAI,CAAA;CACd;AAED,kFAAkF;AAClF,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,WAAY,SAAQ,cAAc;IACjD,EAAE,EAAE,MAAM,CAAA;IACV,iGAAiG;IACjG,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/intent/types.ts"],"names":[],"mappings":""}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BentoFragmentKind } from './types.js';
|
|
1
|
+
import type { BentoFragmentKind, LandingGateFieldKind } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* The shapes a drawn widget can take.
|
|
4
4
|
*
|
|
@@ -30,4 +30,17 @@ export declare const AREA_SECTION_CAP = 6;
|
|
|
30
30
|
* value nothing can draw is a tile with an empty bottom.
|
|
31
31
|
*/
|
|
32
32
|
export declare const BENTO_FRAGMENT_KINDS: BentoFragmentKind[];
|
|
33
|
+
/**
|
|
34
|
+
* The controls a landing-gate field can be — the simplest native inputs, and no picker of any
|
|
35
|
+
* kind. A closed set for the same reason as {@link BENTO_FRAGMENT_KINDS}: each value is one plain
|
|
36
|
+
* element the seed's gate knows how to draw.
|
|
37
|
+
*/
|
|
38
|
+
export declare const LANDING_GATE_FIELD_KINDS: LandingGateFieldKind[];
|
|
39
|
+
/** The most fields a landing gate carries. A form longer than this is a screen, not an entry. */
|
|
40
|
+
export declare const LANDING_GATE_MAX_FIELDS = 3;
|
|
41
|
+
/** The options a `select` field carries: fewer is no choice, more is a list to browse. */
|
|
42
|
+
export declare const LANDING_GATE_OPTIONS: {
|
|
43
|
+
readonly min: 2;
|
|
44
|
+
readonly max: 6;
|
|
45
|
+
};
|
|
33
46
|
//# sourceMappingURL=consts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../../src/scaffold/consts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../../src/scaffold/consts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAEzE;;;;;;;GAOG;AACH,oBAAY,UAAU;IACpB,6DAA6D;IAC7D,IAAI,SAAS;IACb,gEAAgE;IAChE,KAAK,UAAU;IACf,mCAAmC;IACnC,IAAI,SAAS;IACb,yCAAyC;IACzC,KAAK,UAAU;IACf,+DAA+D;IAC/D,IAAI,SAAS;CACd;AAED,yEAAyE;AACzE,eAAO,MAAM,oBAAoB,IAAI,CAAA;AAErC,uEAAuE;AACvE,eAAO,MAAM,gBAAgB,IAAI,CAAA;AAEjC;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,EAAE,iBAAiB,EAAwC,CAAA;AAE5F;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,oBAAoB,EAAyC,CAAA;AAEpG,iGAAiG;AACjG,eAAO,MAAM,uBAAuB,IAAI,CAAA;AAExC,0FAA0F;AAC1F,eAAO,MAAM,oBAAoB;aAAK,GAAG,EAAE,CAAC;aAAE,GAAG,EAAE,CAAC;CAAW,CAAA"}
|
package/build/scaffold/consts.js
CHANGED
|
@@ -30,4 +30,14 @@ export const AREA_SECTION_CAP = 6;
|
|
|
30
30
|
* value nothing can draw is a tile with an empty bottom.
|
|
31
31
|
*/
|
|
32
32
|
export const BENTO_FRAGMENT_KINDS = ['list', 'note', 'people', 'steps'];
|
|
33
|
+
/**
|
|
34
|
+
* The controls a landing-gate field can be — the simplest native inputs, and no picker of any
|
|
35
|
+
* kind. A closed set for the same reason as {@link BENTO_FRAGMENT_KINDS}: each value is one plain
|
|
36
|
+
* element the seed's gate knows how to draw.
|
|
37
|
+
*/
|
|
38
|
+
export const LANDING_GATE_FIELD_KINDS = ['text', 'select', 'number', 'date'];
|
|
39
|
+
/** The most fields a landing gate carries. A form longer than this is a screen, not an entry. */
|
|
40
|
+
export const LANDING_GATE_MAX_FIELDS = 3;
|
|
41
|
+
/** The options a `select` field carries: fewer is no choice, more is a list to browse. */
|
|
42
|
+
export const LANDING_GATE_OPTIONS = { min: 2, max: 6 };
|
|
33
43
|
//# sourceMappingURL=consts.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../../src/scaffold/consts.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,CAAN,IAAY,UAWX;AAXD,WAAY,UAAU;IACpB,6DAA6D;IAC7D,2BAAa,CAAA;IACb,gEAAgE;IAChE,6BAAe,CAAA;IACf,mCAAmC;IACnC,2BAAa,CAAA;IACb,yCAAyC;IACzC,6BAAe,CAAA;IACf,+DAA+D;IAC/D,2BAAa,CAAA;AACf,CAAC,EAXW,UAAU,KAAV,UAAU,QAWrB;AAED,yEAAyE;AACzE,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAA;AAErC,uEAAuE;AACvE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAA;AAEjC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA"}
|
|
1
|
+
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../../src/scaffold/consts.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,CAAN,IAAY,UAWX;AAXD,WAAY,UAAU;IACpB,6DAA6D;IAC7D,2BAAa,CAAA;IACb,gEAAgE;IAChE,6BAAe,CAAA;IACf,mCAAmC;IACnC,2BAAa,CAAA;IACb,yCAAyC;IACzC,6BAAe,CAAA;IACf,+DAA+D;IAC/D,2BAAa,CAAA;AACf,CAAC,EAXW,UAAU,KAAV,UAAU,QAWrB;AAED,yEAAyE;AACzE,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAA;AAErC,uEAAuE;AACvE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAA;AAEjC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;AAE5F;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAA2B,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;AAEpG,iGAAiG;AACjG,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAA;AAExC,0FAA0F;AAC1F,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAW,CAAA"}
|
|
@@ -2,4 +2,11 @@ import type { JSONSchemaType } from 'ajv';
|
|
|
2
2
|
import type { ProductIdentity, ScaffoldPlan } from './types.js';
|
|
3
3
|
export declare const ProductIdentitySchema: JSONSchemaType<ProductIdentity>;
|
|
4
4
|
export declare const ScaffoldPlanSchema: JSONSchemaType<ScaffoldPlan>;
|
|
5
|
+
/**
|
|
6
|
+
* The plan as the planning MODEL answers it — {@link ScaffoldPlanSchema} with the gate held to the
|
|
7
|
+
* form. The stored schema keeps the pre-form gate keys optional so an old plan still validates;
|
|
8
|
+
* offering them to the model would let it write a chip picker again. Typed as the stored plan
|
|
9
|
+
* because every answer is one: the answer's gate is a subset of the stored gate.
|
|
10
|
+
*/
|
|
11
|
+
export declare const ScaffoldPlanAnswerSchema: JSONSchemaType<ScaffoldPlan>;
|
|
5
12
|
//# sourceMappingURL=schemas.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/scaffold/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAA;AAIzC,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/scaffold/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAA;AAIzC,OAAO,KAAK,EAC0E,eAAe,EACjF,YAAY,EAC/B,MAAM,YAAY,CAAA;AA+fnB,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAAC,eAAe,CAkBjE,CAAA;AAED,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,YAAY,CA6B3D,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,EAiBrB,cAAc,CAAC,YAAY,CAAC,CAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ProjectArea } from '../areas/consts.js';
|
|
2
|
-
import { BENTO_FRAGMENT_KINDS, WidgetKind } from './consts.js';
|
|
2
|
+
import { BENTO_FRAGMENT_KINDS, LANDING_GATE_FIELD_KINDS, WidgetKind } from './consts.js';
|
|
3
3
|
const SketchContentSchema = {
|
|
4
4
|
type: 'object',
|
|
5
5
|
title: 'SketchContent',
|
|
@@ -75,7 +75,8 @@ const ScaffoldStoryPlanSchema = {
|
|
|
75
75
|
screen: {
|
|
76
76
|
type: 'string',
|
|
77
77
|
description: 'The main screen this story will need, named "{section}/{purpose}" -'
|
|
78
|
-
+ ' e.g. "Requests/Submit
|
|
78
|
+
+ ' e.g. "Requests/Submit request". The purpose is one or two words, never a sentence.'
|
|
79
|
+
+ ' The section part must equal the section above'
|
|
79
80
|
},
|
|
80
81
|
widget: {
|
|
81
82
|
type: 'object',
|
|
@@ -213,99 +214,125 @@ const BentoFragmentSchema = {
|
|
|
213
214
|
required: ['kind', 'rows'],
|
|
214
215
|
additionalProperties: false,
|
|
215
216
|
};
|
|
216
|
-
const
|
|
217
|
+
const LandingGateFieldSchema = {
|
|
217
218
|
type: 'object',
|
|
218
|
-
title: '
|
|
219
|
-
description: '
|
|
220
|
-
+ ' call-to-action buttons: a guest makes a few picks, sees sample results ranked against them,'
|
|
221
|
-
+ ' and signs in to continue with the picks carried over. Every text is in the end user\'s words',
|
|
219
|
+
title: 'LandingGateField',
|
|
220
|
+
description: 'One simple, non-sensitive field of the landing gate',
|
|
222
221
|
properties: {
|
|
223
|
-
|
|
222
|
+
name: {
|
|
224
223
|
type: 'string',
|
|
225
|
-
description: 'The
|
|
224
|
+
description: 'The camelCase key its value travels under, one word - e.g. "dish"'
|
|
226
225
|
},
|
|
227
|
-
|
|
228
|
-
type: 'string', nullable: true,
|
|
229
|
-
description: 'Leave it out - the platform fills it'
|
|
230
|
-
},
|
|
231
|
-
question: {
|
|
226
|
+
label: {
|
|
232
227
|
type: 'string',
|
|
233
|
-
description: 'The
|
|
234
|
-
+ ' in your pantry?"'
|
|
228
|
+
description: 'The visible label in the end user\'s words, 2-5 words - e.g. "What are you baking?"'
|
|
235
229
|
},
|
|
236
|
-
|
|
237
|
-
label: {
|
|
230
|
+
kind: {
|
|
238
231
|
type: 'string',
|
|
239
|
-
|
|
232
|
+
enum: LANDING_GATE_FIELD_KINDS,
|
|
233
|
+
description: 'text (a short line, the default), select (ONE choice out of 2-6 plain options),'
|
|
234
|
+
+ ' number, or date. Nothing else exists: no chips, no toggles, no multiple choice'
|
|
240
235
|
},
|
|
241
|
-
|
|
242
|
-
type: '
|
|
243
|
-
|
|
244
|
-
description: '5 to 8 things a guest can pick, 1-3 words each, from the story\'s own domain.'
|
|
245
|
-
+ ' Nothing personal, nothing sensitive, nothing that needs an account'
|
|
236
|
+
placeholder: {
|
|
237
|
+
type: 'string', nullable: true,
|
|
238
|
+
description: 'text or number only: an example value, e.g. "Rye sourdough". Never personal'
|
|
246
239
|
},
|
|
247
|
-
|
|
248
|
-
type: 'array',
|
|
240
|
+
options: {
|
|
241
|
+
type: 'array', nullable: true,
|
|
249
242
|
items: { type: 'string' },
|
|
250
|
-
description: '
|
|
251
|
-
+ ' already shows results'
|
|
243
|
+
description: 'select only: 2 to 6 plain options, 1-3 words each'
|
|
252
244
|
},
|
|
245
|
+
},
|
|
246
|
+
required: ['name', 'label', 'kind'],
|
|
247
|
+
additionalProperties: false,
|
|
248
|
+
};
|
|
249
|
+
/** What the gate's texts tell the planning model — one place, shared by the answer and stored schemas. */
|
|
250
|
+
const GATE_TEXT = {
|
|
251
|
+
description: 'The working entry into the key end-user workflow, drawn in the hero instead of the'
|
|
252
|
+
+ ' call-to-action buttons: ONE SMALL FORM - a heading, one to three simple non-sensitive'
|
|
253
|
+
+ ' fields, one button. A guest fills it in, signs in, and continues with what they entered.'
|
|
254
|
+
+ ' No choices to browse, no sample results, no counts, no previews. Every text is in the end'
|
|
255
|
+
+ ' user\'s words',
|
|
256
|
+
story: 'The code of the landing story, copied EXACTLY as given to you. Never shown',
|
|
257
|
+
target: 'Leave it out - the platform fills it',
|
|
258
|
+
question: 'The heading of the form, 3-6 words, in the end user\'s words - e.g. "What are you'
|
|
259
|
+
+ ' baking?"',
|
|
260
|
+
fields: 'The fields, AS FEW AS POSSIBLE: one whenever one is enough, never more than three. They'
|
|
261
|
+
+ ' are the first things the story\'s own screen asks for, in the same words',
|
|
262
|
+
cta: 'The button label, a verb first, 2-3 words and an arrow - e.g. "Start my post →"',
|
|
263
|
+
note: 'One short muted line under the form: no account is needed yet and what was entered comes'
|
|
264
|
+
+ ' along - e.g. "No account needed. What you enter comes with you."',
|
|
265
|
+
};
|
|
266
|
+
/** The pre-form keys of a stored gate: optional, described as history, never offered to the model. */
|
|
267
|
+
const LEGACY_GATE_NOTE = 'Pre-form shape, never written';
|
|
268
|
+
/**
|
|
269
|
+
* What the planning MODEL answers for the gate: the form and nothing older. The stored schema below
|
|
270
|
+
* adds the pre-form keys as optional, so a plan written before the gate became a form still
|
|
271
|
+
* validates; the model is never offered them.
|
|
272
|
+
*/
|
|
273
|
+
const LandingGateAnswerSchema = {
|
|
274
|
+
type: 'object',
|
|
275
|
+
title: 'LandingGatePlan',
|
|
276
|
+
description: GATE_TEXT.description,
|
|
277
|
+
properties: {
|
|
278
|
+
story: { type: 'string', description: GATE_TEXT.story },
|
|
279
|
+
target: { type: 'string', nullable: true, description: GATE_TEXT.target },
|
|
280
|
+
question: { type: 'string', description: GATE_TEXT.question },
|
|
281
|
+
fields: { type: 'array', items: LandingGateFieldSchema, description: GATE_TEXT.fields },
|
|
282
|
+
cta: { type: 'string', description: GATE_TEXT.cta },
|
|
283
|
+
note: { type: 'string', description: GATE_TEXT.note },
|
|
284
|
+
},
|
|
285
|
+
required: ['story', 'question', 'fields', 'cta', 'note'],
|
|
286
|
+
additionalProperties: false,
|
|
287
|
+
};
|
|
288
|
+
/**
|
|
289
|
+
* The gate as a plan may STORE it: the form, plus the pre-form keys (chips, sample records, a live
|
|
290
|
+
* count) as optional. A schema that validates a stored document only ever grows optional.
|
|
291
|
+
*/
|
|
292
|
+
const LandingGatePlanSchema = {
|
|
293
|
+
type: 'object',
|
|
294
|
+
title: 'LandingGatePlan',
|
|
295
|
+
description: GATE_TEXT.description,
|
|
296
|
+
properties: {
|
|
297
|
+
story: { type: 'string', description: GATE_TEXT.story },
|
|
298
|
+
target: { type: 'string', nullable: true, description: GATE_TEXT.target },
|
|
299
|
+
question: { type: 'string', description: GATE_TEXT.question },
|
|
300
|
+
fields: { type: 'array', nullable: true, items: LandingGateFieldSchema, description: GATE_TEXT.fields },
|
|
301
|
+
cta: { type: 'string', description: GATE_TEXT.cta },
|
|
302
|
+
note: { type: 'string', description: GATE_TEXT.note },
|
|
303
|
+
hint: { type: 'string', nullable: true, description: LEGACY_GATE_NOTE },
|
|
304
|
+
label: { type: 'string', nullable: true, description: LEGACY_GATE_NOTE },
|
|
305
|
+
inputs: { type: 'array', nullable: true, items: { type: 'string' }, description: LEGACY_GATE_NOTE },
|
|
306
|
+
selected: { type: 'array', nullable: true, items: { type: 'string' }, description: LEGACY_GATE_NOTE },
|
|
253
307
|
results: {
|
|
254
|
-
type: 'array',
|
|
308
|
+
type: 'array', nullable: true,
|
|
255
309
|
items: {
|
|
256
310
|
type: 'object',
|
|
257
311
|
properties: {
|
|
258
|
-
title: { type: 'string'
|
|
259
|
-
meta: {
|
|
260
|
-
|
|
261
|
-
description: 'One short line: how well it fits and one fact, e.g. "Uses all 3 · 3 h 20 min"'
|
|
262
|
-
},
|
|
263
|
-
needs: {
|
|
264
|
-
type: 'array',
|
|
265
|
-
items: { type: 'string' },
|
|
266
|
-
description: '2-4 of the inputs above, copied exactly, that this record uses'
|
|
267
|
-
},
|
|
312
|
+
title: { type: 'string' },
|
|
313
|
+
meta: { type: 'string' },
|
|
314
|
+
needs: { type: 'array', items: { type: 'string' } },
|
|
268
315
|
},
|
|
269
316
|
required: ['title', 'meta', 'needs'],
|
|
270
317
|
additionalProperties: false,
|
|
271
318
|
},
|
|
272
|
-
description:
|
|
319
|
+
description: LEGACY_GATE_NOTE
|
|
273
320
|
},
|
|
274
321
|
count: {
|
|
275
|
-
type: 'object',
|
|
322
|
+
type: 'object', nullable: true,
|
|
276
323
|
properties: {
|
|
277
|
-
one: { type: 'string'
|
|
278
|
-
many: { type: 'string'
|
|
279
|
-
none: { type: 'string'
|
|
324
|
+
one: { type: 'string' },
|
|
325
|
+
many: { type: 'string' },
|
|
326
|
+
none: { type: 'string' },
|
|
280
327
|
},
|
|
281
328
|
required: ['one', 'many', 'none'],
|
|
282
329
|
additionalProperties: false,
|
|
283
|
-
description:
|
|
284
|
-
},
|
|
285
|
-
empty: {
|
|
286
|
-
type: 'string',
|
|
287
|
-
description: 'One line shown when nothing matches, saying what to pick - e.g. "Pick two or more'
|
|
288
|
-
+ ' ingredients to see what you can bake."'
|
|
289
|
-
},
|
|
290
|
-
note: {
|
|
291
|
-
type: 'string',
|
|
292
|
-
description: 'One short line beside the button: signing in is by email and the picks come'
|
|
293
|
-
+ ' along - e.g. "Sign in with your email. Your picks come with you."'
|
|
294
|
-
},
|
|
295
|
-
cta: {
|
|
296
|
-
type: 'string',
|
|
297
|
-
description: 'The button label, a verb first, 2-3 words and an arrow - e.g. "Open recipes →"'
|
|
298
|
-
},
|
|
299
|
-
lock: {
|
|
300
|
-
type: 'string',
|
|
301
|
-
description: 'What unlocks after sign-in, as the accessible name of a lock icon - e.g. "Full'
|
|
302
|
-
+ ' method after sign-in"'
|
|
330
|
+
description: LEGACY_GATE_NOTE
|
|
303
331
|
},
|
|
332
|
+
empty: { type: 'string', nullable: true, description: LEGACY_GATE_NOTE },
|
|
333
|
+
lock: { type: 'string', nullable: true, description: LEGACY_GATE_NOTE },
|
|
304
334
|
},
|
|
305
|
-
required: [
|
|
306
|
-
'story', 'question', 'hint', 'label', 'inputs', 'selected', 'results', 'count', 'empty',
|
|
307
|
-
'note', 'cta', 'lock',
|
|
308
|
-
],
|
|
335
|
+
required: ['story', 'question', 'cta', 'note'],
|
|
309
336
|
additionalProperties: false,
|
|
310
337
|
};
|
|
311
338
|
const GuestHomePlanSchema = {
|
|
@@ -520,4 +547,28 @@ export const ScaffoldPlanSchema = {
|
|
|
520
547
|
required: ['identity', 'guestHome', 'areas', 'stories', 'motifs'],
|
|
521
548
|
additionalProperties: false,
|
|
522
549
|
};
|
|
550
|
+
/**
|
|
551
|
+
* The plan as the planning MODEL answers it — {@link ScaffoldPlanSchema} with the gate held to the
|
|
552
|
+
* form. The stored schema keeps the pre-form gate keys optional so an old plan still validates;
|
|
553
|
+
* offering them to the model would let it write a chip picker again. Typed as the stored plan
|
|
554
|
+
* because every answer is one: the answer's gate is a subset of the stored gate.
|
|
555
|
+
*/
|
|
556
|
+
export const ScaffoldPlanAnswerSchema = {
|
|
557
|
+
...ScaffoldPlanSchema,
|
|
558
|
+
properties: {
|
|
559
|
+
...ScaffoldPlanSchema.properties,
|
|
560
|
+
guestHome: {
|
|
561
|
+
...GuestHomePlanSchema,
|
|
562
|
+
properties: {
|
|
563
|
+
...GuestHomePlanSchema.properties,
|
|
564
|
+
gate: {
|
|
565
|
+
...LandingGateAnswerSchema,
|
|
566
|
+
nullable: true,
|
|
567
|
+
description: 'ONLY when you were given a landing story; omit it otherwise. '
|
|
568
|
+
+ LandingGateAnswerSchema.description,
|
|
569
|
+
},
|
|
570
|
+
},
|
|
571
|
+
},
|
|
572
|
+
},
|
|
573
|
+
};
|
|
523
574
|
//# sourceMappingURL=schemas.js.map
|