@swell/cli 2.6.0 → 2.7.1
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/commands/app/frontend/dev.js +18 -0
- package/dist/commands/inspect/content.d.ts +25 -14
- package/dist/commands/inspect/content.js +34 -144
- package/dist/commands/inspect/extensions.d.ts +49 -0
- package/dist/commands/inspect/extensions.js +424 -0
- package/dist/commands/inspect/functions.d.ts +48 -0
- package/dist/commands/inspect/functions.js +83 -0
- package/dist/commands/inspect/index.js +8 -7
- package/dist/commands/inspect/models.d.ts +17 -2
- package/dist/commands/inspect/models.js +114 -47
- package/dist/commands/inspect/notifications.d.ts +31 -0
- package/dist/commands/inspect/notifications.js +125 -0
- package/dist/commands/inspect/settings.d.ts +28 -0
- package/dist/commands/inspect/settings.js +82 -0
- package/dist/commands/inspect/webhooks.d.ts +34 -0
- package/dist/commands/inspect/webhooks.js +61 -0
- package/dist/create-app-command.d.ts +7 -0
- package/dist/create-app-command.js +80 -9
- package/dist/inspect-resource-command.d.ts +91 -0
- package/dist/inspect-resource-command.js +232 -0
- package/dist/lib/apps/index.d.ts +2 -1
- package/dist/lib/apps/index.js +43 -6
- package/dist/lib/apps/inspect-scope.d.ts +58 -0
- package/dist/lib/apps/inspect-scope.js +60 -0
- package/dist/lib/apps/object-id.d.ts +7 -0
- package/dist/lib/apps/object-id.js +9 -0
- package/dist/lib/apps/paths.js +8 -1
- package/dist/lib/apps/resolve.d.ts +16 -0
- package/dist/lib/apps/resolve.js +39 -0
- package/dist/lib/apps/slug.d.ts +29 -0
- package/dist/lib/apps/slug.js +12 -0
- package/dist/lib/inspect/content.d.ts +39 -0
- package/dist/lib/inspect/content.js +76 -0
- package/dist/lib/inspect/extensions.d.ts +267 -0
- package/dist/lib/inspect/extensions.js +690 -0
- package/dist/lib/inspect/notifications.d.ts +115 -0
- package/dist/lib/inspect/notifications.js +173 -0
- package/dist/lib/inspect/settings.d.ts +61 -0
- package/dist/lib/inspect/settings.js +56 -0
- package/dist/lib/inspect/table.d.ts +29 -0
- package/dist/lib/inspect/table.js +61 -0
- package/dist/push-app-command.js +3 -2
- package/dist/swell-api-command.d.ts +0 -4
- package/dist/swell-api-command.js +2 -18
- package/oclif.manifest.json +392 -35
- package/package.json +1 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { HEX24 } from './object-id.js';
|
|
2
|
+
export class ScopeError extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = 'ScopeError';
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the scope of an `inspect` subcommand.
|
|
10
|
+
*
|
|
11
|
+
* Grammar:
|
|
12
|
+
* (no --app) → global listing, no filter
|
|
13
|
+
* --app=<slug> → filter by the named app
|
|
14
|
+
* --app=. → filter by the app in the current directory's swell.json
|
|
15
|
+
*
|
|
16
|
+
* The `.` sentinel is the only place swell.json influences behavior; the
|
|
17
|
+
* default is always global so the same invocation produces the same output
|
|
18
|
+
* regardless of cwd.
|
|
19
|
+
*/
|
|
20
|
+
export async function resolveInspectScope(ctx, flags = {}) {
|
|
21
|
+
if (!flags.app) {
|
|
22
|
+
return { query: {} };
|
|
23
|
+
}
|
|
24
|
+
if (flags.app === '.') {
|
|
25
|
+
const slug = await ctx.readCurrentAppSlug();
|
|
26
|
+
if (!slug) {
|
|
27
|
+
throw new ScopeError(`--app=. requires a swell.json with an 'id' field in the current directory. Pass --app=<slug> or cd into an app.`);
|
|
28
|
+
}
|
|
29
|
+
const appId = await ctx.resolveAppId(slug);
|
|
30
|
+
return { appId, appSlug: slug, query: { app_id: appId } };
|
|
31
|
+
}
|
|
32
|
+
const appId = await ctx.resolveAppId(flags.app);
|
|
33
|
+
return { appId, appSlug: flags.app, query: { app_id: appId } };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Classify an inspect-subcommand identifier argument.
|
|
37
|
+
*
|
|
38
|
+
* - `id`: 24-char hex (Mongo ObjectId)
|
|
39
|
+
* - `slug`: dotted form `app.<appPart>.<name>` where appPart can be either
|
|
40
|
+
* an ObjectId or a public/private app slug
|
|
41
|
+
* - `name`: bare identifier (letters, digits, hyphen, underscore) —
|
|
42
|
+
* requires app context to resolve
|
|
43
|
+
*
|
|
44
|
+
* Resources whose stored ids are non-hex dotted strings (notifications)
|
|
45
|
+
* handle dispatch in their own `showDetail` override; the base classifier
|
|
46
|
+
* stays narrow.
|
|
47
|
+
*/
|
|
48
|
+
export function classifyIdentifier(identifier) {
|
|
49
|
+
if (HEX24.test(identifier)) {
|
|
50
|
+
return { kind: 'id', id: identifier };
|
|
51
|
+
}
|
|
52
|
+
const slugMatch = identifier.match(/^app\.([^.]+)\.(.+)$/);
|
|
53
|
+
if (slugMatch) {
|
|
54
|
+
return { kind: 'slug', appPart: slugMatch[1], name: slugMatch[2] };
|
|
55
|
+
}
|
|
56
|
+
if (/^[\w-]+$/i.test(identifier)) {
|
|
57
|
+
return { kind: 'name', name: identifier };
|
|
58
|
+
}
|
|
59
|
+
return { kind: 'invalid', input: identifier };
|
|
60
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mongo ObjectId shape — 24 hex chars, case-insensitive. The CLI accepts
|
|
3
|
+
* either-case input (curl/jq users tend to lowercase, admin URLs sometimes
|
|
4
|
+
* upper). All inspect grammars use the same pattern, so it lives here.
|
|
5
|
+
*/
|
|
6
|
+
export declare const HEX24: RegExp;
|
|
7
|
+
export declare function isObjectId(value: string): boolean;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mongo ObjectId shape — 24 hex chars, case-insensitive. The CLI accepts
|
|
3
|
+
* either-case input (curl/jq users tend to lowercase, admin URLs sometimes
|
|
4
|
+
* upper). All inspect grammars use the same pattern, so it lives here.
|
|
5
|
+
*/
|
|
6
|
+
export const HEX24 = /^[\da-f]{24}$/i;
|
|
7
|
+
export function isObjectId(value) {
|
|
8
|
+
return HEX24.test(value);
|
|
9
|
+
}
|
package/dist/lib/apps/paths.js
CHANGED
|
@@ -9,13 +9,20 @@ function globOptions(appPath, options) {
|
|
|
9
9
|
gitignore: true,
|
|
10
10
|
...options,
|
|
11
11
|
expandDirectories: true,
|
|
12
|
-
//
|
|
12
|
+
// .dev.vars can hold real credentials — ignored here in case the
|
|
13
|
+
// user's .gitignore doesn't cover it.
|
|
13
14
|
ignore: [
|
|
14
15
|
'**/.swellrc',
|
|
15
16
|
'**/.git/**',
|
|
16
17
|
'**/node_modules/**',
|
|
17
18
|
'**/package-lock.json',
|
|
18
19
|
'**/yarn.lock',
|
|
20
|
+
'**/pnpm-lock.yaml',
|
|
21
|
+
'**/bun.lockb',
|
|
22
|
+
'**/bun.lock',
|
|
23
|
+
'**/.wrangler/**',
|
|
24
|
+
'**/.dev.vars',
|
|
25
|
+
'**/.dev.vars.*',
|
|
19
26
|
...(options?.ignore || []),
|
|
20
27
|
],
|
|
21
28
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import Api from '../api.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve an app's ObjectId from a public slug or private slug; pass through
|
|
4
|
+
* a value that already looks like an ObjectId.
|
|
5
|
+
*
|
|
6
|
+
* Priority mirrors schema-api-server installer.js:3596 `getAppBySlugId`:
|
|
7
|
+
* private match (`_<slug>`) wins over public match. For typical apps the
|
|
8
|
+
* order is invisible (a single record matches both branches), but it
|
|
9
|
+
* matters when two apps share a slug across publish state.
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveAppId(api: Api, appIdOrSlug: string): Promise<string>;
|
|
12
|
+
/**
|
|
13
|
+
* Build a map of installed-app ObjectId -> friendly slug, using the shared
|
|
14
|
+
* server-canonical slug priority (see `slug.ts`).
|
|
15
|
+
*/
|
|
16
|
+
export declare function buildAppSlugMap(api: Api): Promise<Record<string, string>>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { isObjectId } from './object-id.js';
|
|
2
|
+
import { slugFromInstalledApp } from './slug.js';
|
|
3
|
+
/**
|
|
4
|
+
* Resolve an app's ObjectId from a public slug or private slug; pass through
|
|
5
|
+
* a value that already looks like an ObjectId.
|
|
6
|
+
*
|
|
7
|
+
* Priority mirrors schema-api-server installer.js:3596 `getAppBySlugId`:
|
|
8
|
+
* private match (`_<slug>`) wins over public match. For typical apps the
|
|
9
|
+
* order is invisible (a single record matches both branches), but it
|
|
10
|
+
* matters when two apps share a slug across publish state.
|
|
11
|
+
*/
|
|
12
|
+
export async function resolveAppId(api, appIdOrSlug) {
|
|
13
|
+
if (isObjectId(appIdOrSlug)) {
|
|
14
|
+
return appIdOrSlug;
|
|
15
|
+
}
|
|
16
|
+
const installedApps = await api.get({ adminPath: `/client/apps` });
|
|
17
|
+
const records = (installedApps?.results ?? []);
|
|
18
|
+
const privateMatch = records.find((a) => a.app_private_id === `_${appIdOrSlug}`);
|
|
19
|
+
const app = privateMatch ?? records.find((a) => a.app_public_id === appIdOrSlug);
|
|
20
|
+
if (!app) {
|
|
21
|
+
throw new Error(`App '${appIdOrSlug}' not found`);
|
|
22
|
+
}
|
|
23
|
+
return app.app_id;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build a map of installed-app ObjectId -> friendly slug, using the shared
|
|
27
|
+
* server-canonical slug priority (see `slug.ts`).
|
|
28
|
+
*/
|
|
29
|
+
export async function buildAppSlugMap(api) {
|
|
30
|
+
const installedApps = await api.get({ adminPath: `/client/apps` });
|
|
31
|
+
const map = {};
|
|
32
|
+
for (const a of (installedApps?.results ?? [])) {
|
|
33
|
+
const slug = slugFromInstalledApp(a);
|
|
34
|
+
if (a.app_id && slug) {
|
|
35
|
+
map[a.app_id] = slug;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return map;
|
|
39
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute an app's display slug.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the server-canonical priority in
|
|
5
|
+
* schema-api-server/api/admin/features/apps/installer.js:213 — private_id
|
|
6
|
+
* (with the leading underscore stripped) wins over public_id. The orders
|
|
7
|
+
* agree for the typical case where `private_id === '_' + public_id`, but
|
|
8
|
+
* diverge for theme-collision suffixes (`_my_theme_1`) and any app where
|
|
9
|
+
* `public_id` was set independently of `private_id`. Following the server
|
|
10
|
+
* keeps CLI labels in sync with what the admin UI shows for the same row.
|
|
11
|
+
*
|
|
12
|
+
* No id fallback: an app with neither slug returns `undefined`. Callers
|
|
13
|
+
* that want a "show something" guarantee (e.g. the push-app picker) should
|
|
14
|
+
* append `?? app.id` themselves; the slug map deliberately filters such
|
|
15
|
+
* records out so downstream code can flag them as `<not resolved>`.
|
|
16
|
+
*/
|
|
17
|
+
export interface SlugSource {
|
|
18
|
+
privateId?: string;
|
|
19
|
+
publicId?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function appSlug(source: SlugSource): string | undefined;
|
|
22
|
+
export declare function slugFromApp(app: {
|
|
23
|
+
private_id?: string;
|
|
24
|
+
public_id?: string;
|
|
25
|
+
}): string | undefined;
|
|
26
|
+
export declare function slugFromInstalledApp(rec: {
|
|
27
|
+
app_private_id?: string;
|
|
28
|
+
app_public_id?: string;
|
|
29
|
+
}): string | undefined;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function appSlug(source) {
|
|
2
|
+
return source.privateId?.replace(/^_/, '') || source.publicId;
|
|
3
|
+
}
|
|
4
|
+
export function slugFromApp(app) {
|
|
5
|
+
return appSlug({ privateId: app.private_id, publicId: app.public_id });
|
|
6
|
+
}
|
|
7
|
+
export function slugFromInstalledApp(rec) {
|
|
8
|
+
return appSlug({
|
|
9
|
+
privateId: rec.app_private_id,
|
|
10
|
+
publicId: rec.app_public_id,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { GroupInfo } from './table.js';
|
|
2
|
+
export interface ParsedContentId {
|
|
3
|
+
middle: string;
|
|
4
|
+
name: string;
|
|
5
|
+
prefix: 'app' | 'custom';
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Parse a content view identifier of the form `app.<appPart>.<name>` or
|
|
9
|
+
* `custom.<source>.<name>`. Returns null for anything that doesn't match.
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseContentId(id: string): ParsedContentId | null;
|
|
12
|
+
/**
|
|
13
|
+
* Produce the column-1 paste-back key for a content view. Rewrites
|
|
14
|
+
* `app.<hex>.<name>` to `app.<slug>.<name>` when the slug is known; passes
|
|
15
|
+
* `custom.*` and unresolvable app ids through unchanged (both remain valid
|
|
16
|
+
* paste-back forms against the detail endpoint).
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveContentKey(id: string, appSlugById: Record<string, string>): string;
|
|
19
|
+
/**
|
|
20
|
+
* Group a content view record for the vertical split. Records with no
|
|
21
|
+
* `app_id` belong to the platform's `custom.*` source and land in a
|
|
22
|
+
* single `<custom>` section at the top of the output.
|
|
23
|
+
*/
|
|
24
|
+
export declare function groupContentRecord(record: {
|
|
25
|
+
app_id?: string;
|
|
26
|
+
}, appSlugById: Record<string, string>): GroupInfo;
|
|
27
|
+
/**
|
|
28
|
+
* Translate a user-supplied content identifier into the unresolved API
|
|
29
|
+
* form. Accepts:
|
|
30
|
+
* - `custom.<source>.<name>` → unchanged
|
|
31
|
+
* - `app.<hex>.<name>` → unchanged (already unresolved form)
|
|
32
|
+
* - `app.<slug>.<name>` → slug resolved to hex
|
|
33
|
+
* - bare `<name>` → combined with scope.appId to `app.<hex>.<name>`
|
|
34
|
+
*
|
|
35
|
+
* `resolveAppId` is injected so this function is testable without a live API.
|
|
36
|
+
*/
|
|
37
|
+
export declare function reverseResolveContentId(identifier: string, scope: {
|
|
38
|
+
appId?: string;
|
|
39
|
+
}, resolveAppId: (slug: string) => Promise<string>): Promise<string>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a content view identifier of the form `app.<appPart>.<name>` or
|
|
3
|
+
* `custom.<source>.<name>`. Returns null for anything that doesn't match.
|
|
4
|
+
*/
|
|
5
|
+
export function parseContentId(id) {
|
|
6
|
+
const match = id.match(/^(app|custom)\.([^.]+)\.(.+)$/);
|
|
7
|
+
if (!match)
|
|
8
|
+
return null;
|
|
9
|
+
return {
|
|
10
|
+
prefix: match[1],
|
|
11
|
+
middle: match[2],
|
|
12
|
+
name: match[3],
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Produce the column-1 paste-back key for a content view. Rewrites
|
|
17
|
+
* `app.<hex>.<name>` to `app.<slug>.<name>` when the slug is known; passes
|
|
18
|
+
* `custom.*` and unresolvable app ids through unchanged (both remain valid
|
|
19
|
+
* paste-back forms against the detail endpoint).
|
|
20
|
+
*/
|
|
21
|
+
export function resolveContentKey(id, appSlugById) {
|
|
22
|
+
const parsed = parseContentId(id);
|
|
23
|
+
if (!parsed)
|
|
24
|
+
return id;
|
|
25
|
+
if (parsed.prefix === 'custom')
|
|
26
|
+
return id;
|
|
27
|
+
const slug = appSlugById[parsed.middle] ?? parsed.middle;
|
|
28
|
+
return `app.${slug}.${parsed.name}`;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Group a content view record for the vertical split. Records with no
|
|
32
|
+
* `app_id` belong to the platform's `custom.*` source and land in a
|
|
33
|
+
* single `<custom>` section at the top of the output.
|
|
34
|
+
*/
|
|
35
|
+
export function groupContentRecord(record, appSlugById) {
|
|
36
|
+
if (!record.app_id) {
|
|
37
|
+
return { slug: '<custom>', label: 'custom', order: 0 };
|
|
38
|
+
}
|
|
39
|
+
const resolved = appSlugById[record.app_id];
|
|
40
|
+
if (!resolved) {
|
|
41
|
+
return { slug: '<not resolved>', order: 2 };
|
|
42
|
+
}
|
|
43
|
+
return { slug: resolved };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Translate a user-supplied content identifier into the unresolved API
|
|
47
|
+
* form. Accepts:
|
|
48
|
+
* - `custom.<source>.<name>` → unchanged
|
|
49
|
+
* - `app.<hex>.<name>` → unchanged (already unresolved form)
|
|
50
|
+
* - `app.<slug>.<name>` → slug resolved to hex
|
|
51
|
+
* - bare `<name>` → combined with scope.appId to `app.<hex>.<name>`
|
|
52
|
+
*
|
|
53
|
+
* `resolveAppId` is injected so this function is testable without a live API.
|
|
54
|
+
*/
|
|
55
|
+
export async function reverseResolveContentId(identifier, scope, resolveAppId) {
|
|
56
|
+
if (/^custom\.[^.]+\..+$/.test(identifier)) {
|
|
57
|
+
return identifier;
|
|
58
|
+
}
|
|
59
|
+
if (/^app\.[\da-f]{24}\..+$/i.test(identifier)) {
|
|
60
|
+
return identifier;
|
|
61
|
+
}
|
|
62
|
+
const appMatch = identifier.match(/^app\.([^.]+)\.(.+)$/);
|
|
63
|
+
if (appMatch) {
|
|
64
|
+
const appId = await resolveAppId(appMatch[1]);
|
|
65
|
+
return `app.${appId}.${appMatch[2]}`;
|
|
66
|
+
}
|
|
67
|
+
if (/^[\w-]+$/i.test(identifier)) {
|
|
68
|
+
if (!scope.appId) {
|
|
69
|
+
throw new Error(`Bare content name '${identifier}' requires --app=<slug> or --app=. to scope. ` +
|
|
70
|
+
`Alternatively, pass a full identifier (app.<app>.<name> or custom.<source>.<name>).`);
|
|
71
|
+
}
|
|
72
|
+
return `app.${scope.appId}.${identifier}`;
|
|
73
|
+
}
|
|
74
|
+
throw new Error(`Invalid content identifier '${identifier}'. ` +
|
|
75
|
+
`Expected bare name, app.<app>.<name>, or custom.<source>.<name>.`);
|
|
76
|
+
}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { GroupInfo } from './table.js';
|
|
2
|
+
/**
|
|
3
|
+
* Manifest entry shape, matching `extensions[]` in the deployed app record
|
|
4
|
+
* and in local `./swell.json`. Mirrors the schema at
|
|
5
|
+
* schema-api-server/api/admin/models/apps.json:429-491.
|
|
6
|
+
*/
|
|
7
|
+
export interface ManifestEntry {
|
|
8
|
+
id: string;
|
|
9
|
+
type: 'payment' | 'tax' | 'shipping';
|
|
10
|
+
name?: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
setting?: string;
|
|
13
|
+
subscriptions?: boolean;
|
|
14
|
+
method?: string;
|
|
15
|
+
gateway?: string;
|
|
16
|
+
method_logo_src?: string;
|
|
17
|
+
method_icon_src?: string;
|
|
18
|
+
gateway_logo_src?: string;
|
|
19
|
+
gateway_icon_src?: string;
|
|
20
|
+
carrier?: string;
|
|
21
|
+
carrier_logo_src?: string;
|
|
22
|
+
carrier_icon_src?: string;
|
|
23
|
+
[key: string]: unknown;
|
|
24
|
+
}
|
|
25
|
+
/** CLI-resolved type. Diverges from raw manifest `type` for payment (card vs alt). */
|
|
26
|
+
export type ExtensionType = 'card' | 'alt' | 'shipping' | 'tax';
|
|
27
|
+
export type ExtensionStatus = 'not deployed' | 'not activated' | 'app id mismatch' | 'id mismatch' | 'not selected' | 'gateway missing' | 'not enabled' | 'no handler' | 'handler mismatch' | 'activated';
|
|
28
|
+
export type ActionOwner = 'dev' | 'merchant' | null;
|
|
29
|
+
export interface FunctionRecord {
|
|
30
|
+
id?: string;
|
|
31
|
+
name?: string;
|
|
32
|
+
enabled?: boolean;
|
|
33
|
+
app_id?: string;
|
|
34
|
+
extension?: string;
|
|
35
|
+
model?: {
|
|
36
|
+
events?: string[];
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export interface ComponentRecord {
|
|
40
|
+
id?: string;
|
|
41
|
+
name?: string;
|
|
42
|
+
file_path?: string;
|
|
43
|
+
values?: {
|
|
44
|
+
[key: string]: unknown;
|
|
45
|
+
extension?: string;
|
|
46
|
+
};
|
|
47
|
+
[key: string]: unknown;
|
|
48
|
+
}
|
|
49
|
+
export interface PaymentMethodRecord {
|
|
50
|
+
id?: string;
|
|
51
|
+
name?: string;
|
|
52
|
+
gateway?: string;
|
|
53
|
+
extension_app_id?: string;
|
|
54
|
+
extension_config_id?: string;
|
|
55
|
+
enabled?: boolean;
|
|
56
|
+
activated?: boolean;
|
|
57
|
+
[key: string]: unknown;
|
|
58
|
+
}
|
|
59
|
+
export interface PaymentGatewayRecord {
|
|
60
|
+
id?: string;
|
|
61
|
+
name?: string;
|
|
62
|
+
extension_app_id?: string;
|
|
63
|
+
extension_config_id?: string;
|
|
64
|
+
[key: string]: unknown;
|
|
65
|
+
}
|
|
66
|
+
export interface ShippingCarrierRecord {
|
|
67
|
+
id?: string;
|
|
68
|
+
name?: string;
|
|
69
|
+
enabled?: boolean;
|
|
70
|
+
extension_app_id?: string;
|
|
71
|
+
extension_config_id?: string;
|
|
72
|
+
[key: string]: unknown;
|
|
73
|
+
}
|
|
74
|
+
export interface TaxSettingsRecord {
|
|
75
|
+
extension_app_id?: string;
|
|
76
|
+
extension_config_id?: string;
|
|
77
|
+
[key: string]: unknown;
|
|
78
|
+
}
|
|
79
|
+
export interface PaymentSettings {
|
|
80
|
+
methods?: PaymentMethodRecord[];
|
|
81
|
+
gateways?: PaymentGatewayRecord[];
|
|
82
|
+
[key: string]: unknown;
|
|
83
|
+
}
|
|
84
|
+
export interface ShippingSettings {
|
|
85
|
+
carriers?: ShippingCarrierRecord[];
|
|
86
|
+
[key: string]: unknown;
|
|
87
|
+
}
|
|
88
|
+
export interface NativeBinding {
|
|
89
|
+
path: string;
|
|
90
|
+
record: unknown;
|
|
91
|
+
field_checks: Record<string, boolean>;
|
|
92
|
+
}
|
|
93
|
+
export interface BoundFunction {
|
|
94
|
+
id?: string;
|
|
95
|
+
name?: string;
|
|
96
|
+
extension?: string;
|
|
97
|
+
enabled?: boolean;
|
|
98
|
+
model?: {
|
|
99
|
+
events?: string[];
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
export interface BoundComponent {
|
|
103
|
+
id?: string;
|
|
104
|
+
name?: string;
|
|
105
|
+
extension?: string;
|
|
106
|
+
file_path?: string;
|
|
107
|
+
}
|
|
108
|
+
export interface LocalDiff {
|
|
109
|
+
changed_fields: string[];
|
|
110
|
+
local: ManifestEntry;
|
|
111
|
+
deployed: ManifestEntry;
|
|
112
|
+
}
|
|
113
|
+
export interface ExtensionDetail {
|
|
114
|
+
status: ExtensionStatus;
|
|
115
|
+
action_owner: ActionOwner;
|
|
116
|
+
action: string | null;
|
|
117
|
+
type: ExtensionType | null;
|
|
118
|
+
manifest: ManifestEntry | null;
|
|
119
|
+
native_bindings: NativeBinding[];
|
|
120
|
+
bound: {
|
|
121
|
+
components: BoundComponent[];
|
|
122
|
+
functions: BoundFunction[];
|
|
123
|
+
};
|
|
124
|
+
required_events: string[];
|
|
125
|
+
missing_required_events: string[];
|
|
126
|
+
local_diff: LocalDiff | null;
|
|
127
|
+
}
|
|
128
|
+
/** Parsed identifier for `swell inspect extensions <id>`. */
|
|
129
|
+
export type ParsedExtensionKey = {
|
|
130
|
+
appPart: string;
|
|
131
|
+
extId: string;
|
|
132
|
+
kind: 'slug';
|
|
133
|
+
} | {
|
|
134
|
+
kind: 'name';
|
|
135
|
+
name: string;
|
|
136
|
+
} | {
|
|
137
|
+
input: string;
|
|
138
|
+
kind: 'invalid';
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Parse a `swell inspect extensions` identifier.
|
|
142
|
+
*
|
|
143
|
+
* Accepts:
|
|
144
|
+
* - `app.<slug>.<extId>` → kind 'slug'
|
|
145
|
+
* - bare `<extId>` → kind 'name' (requires --app= scope at the call site)
|
|
146
|
+
*
|
|
147
|
+
* 24-char hex is intentionally NOT a valid form — the synthesized resource has
|
|
148
|
+
* no canonical 24-char id. Callers should reject hex shapes upstream so the
|
|
149
|
+
* error message can be clear about the cause.
|
|
150
|
+
*/
|
|
151
|
+
export declare function parseExtensionKey(input: string): ParsedExtensionKey;
|
|
152
|
+
/** Build the column-1 paste-back key for a row. */
|
|
153
|
+
export declare function formatExtensionKey(slug: string, extId: string): string;
|
|
154
|
+
/**
|
|
155
|
+
* Resolve a manifest entry to the CLI's type taxonomy. Payment splits on
|
|
156
|
+
* `method`: when `method === 'card'` the extension provides a card gateway;
|
|
157
|
+
* otherwise it's an alt method. The platform formula at
|
|
158
|
+
* schema-api-server/api/admin/models/apps.json:457-458 defaults `method` to
|
|
159
|
+
* `id` when unset, so a payment extension whose `id` happens to be `card`
|
|
160
|
+
* resolves to `card` even with no explicit `method`.
|
|
161
|
+
*/
|
|
162
|
+
export declare function resolveExtensionType(entry: Pick<ManifestEntry, 'type' | 'method' | 'id'>): ExtensionType;
|
|
163
|
+
/** Apply the platform's `if(method, method, id)` formula. */
|
|
164
|
+
export declare function paymentMethodIdFor(entry: Pick<ManifestEntry, 'method' | 'id'>): string;
|
|
165
|
+
/** Shipping records use `app_<appId>_<extId>` as both the row id and the carrier id. */
|
|
166
|
+
export declare function shippingBindId(appId: string, extId: string): string;
|
|
167
|
+
/** Card-gateway records use the same id form as shipping rows. */
|
|
168
|
+
export declare function gatewayBindId(appId: string, extId: string): string;
|
|
169
|
+
/**
|
|
170
|
+
* Required events per extension type, in the `<model>/<event>` form that
|
|
171
|
+
* function records actually store.
|
|
172
|
+
*
|
|
173
|
+
* Verified against:
|
|
174
|
+
* schema-api-server/server/vault.js:1818,1894 (payment.create_intent)
|
|
175
|
+
* schema-api-server/api/com/features/payments/index.js:701-757
|
|
176
|
+
* (payment.charge, payment.refund, dispatcher gate)
|
|
177
|
+
* schema-api-server/server/vault.js:2304-2322 (card-gateway intent path)
|
|
178
|
+
* schema-api-server/api/com/features/orders/shipping.js:411-417,424-431,452-458
|
|
179
|
+
* (shipping dispatch + enabled gate)
|
|
180
|
+
* schema-api-server/api/com/features/orders/taxes.js:123-176
|
|
181
|
+
* (tax dispatch)
|
|
182
|
+
* schema-api-server/api/com/features/orders/extensions.test.js:85,95,179,271,290,303,671,704
|
|
183
|
+
* (event-name format fixtures)
|
|
184
|
+
*/
|
|
185
|
+
export declare function requiredEventsFor(type: ExtensionType): string[];
|
|
186
|
+
/**
|
|
187
|
+
* Strip `before:` / `after:` hook-type prefix from an event identifier.
|
|
188
|
+
*
|
|
189
|
+
* Both prefixes are platform-supported on function records; bare events
|
|
190
|
+
* default to `after`. For required-event coverage we treat any prefix as
|
|
191
|
+
* equivalent so that a function declaring `before:payment.charge` covers a
|
|
192
|
+
* required `payments/payment.charge`. Cite:
|
|
193
|
+
* schema-api-server/api/com/features/orders/extensions.test.js:95.
|
|
194
|
+
*/
|
|
195
|
+
export declare function stripHookPrefix(event: string): string;
|
|
196
|
+
/**
|
|
197
|
+
* Compute the set of bare event identifiers covered by a function record's
|
|
198
|
+
* `model.events` list. The function event format is `<model>/<event>` with
|
|
199
|
+
* an optional `before:`/`after:` prefix on the bare event part. Returns the
|
|
200
|
+
* full `<model>/<event>` string with hook prefixes stripped from the event.
|
|
201
|
+
*/
|
|
202
|
+
export declare function eventsCoveredByFunctions(functions: Pick<FunctionRecord, 'model'>[]): Set<string>;
|
|
203
|
+
/** Required events not covered by any of the bound functions. */
|
|
204
|
+
export declare function missingRequiredEvents(type: ExtensionType, functions: Pick<FunctionRecord, 'model'>[]): string[];
|
|
205
|
+
/** Lift a component record into the synthesized `BoundComponent` shape. */
|
|
206
|
+
export declare function liftBoundComponent(record: ComponentRecord): BoundComponent;
|
|
207
|
+
/** Lift a function record into the synthesized `BoundFunction` shape. */
|
|
208
|
+
export declare function liftBoundFunction(record: FunctionRecord): BoundFunction;
|
|
209
|
+
/**
|
|
210
|
+
* Compare a local manifest entry against the deployed entry. Uses deep
|
|
211
|
+
* equality for change detection and reports differing top-level keys. When
|
|
212
|
+
* the entries are equal, returns `null`.
|
|
213
|
+
*/
|
|
214
|
+
export declare function diffManifestEntries(local: ManifestEntry, deployed: ManifestEntry): LocalDiff | null;
|
|
215
|
+
export interface BuildDetailInput {
|
|
216
|
+
appId: string;
|
|
217
|
+
appSlug: string;
|
|
218
|
+
extId: string;
|
|
219
|
+
manifest: ManifestEntry | null;
|
|
220
|
+
/** True when the local manifest has the entry but the deployed app record doesn't. */
|
|
221
|
+
notDeployed?: boolean;
|
|
222
|
+
payments?: PaymentSettings | null;
|
|
223
|
+
shipments?: ShippingSettings | null;
|
|
224
|
+
taxes?: TaxSettingsRecord | null;
|
|
225
|
+
/** Functions in the same app whose top-level `extension` matches this extId. */
|
|
226
|
+
functions: FunctionRecord[];
|
|
227
|
+
/** Components in the same app whose `values.extension` matches this extId. */
|
|
228
|
+
components: ComponentRecord[];
|
|
229
|
+
localDiff?: LocalDiff | null;
|
|
230
|
+
}
|
|
231
|
+
export interface BuildOrphanInput {
|
|
232
|
+
appId: string;
|
|
233
|
+
appSlug: string;
|
|
234
|
+
/** The unresolved extension value taken from the function/component. */
|
|
235
|
+
unresolvedId: string;
|
|
236
|
+
functions: FunctionRecord[];
|
|
237
|
+
components: ComponentRecord[];
|
|
238
|
+
}
|
|
239
|
+
/** Build the synthesized envelope for a regular (non-orphan) extension row. */
|
|
240
|
+
export declare function buildExtensionDetail(input: BuildDetailInput): ExtensionDetail;
|
|
241
|
+
/** Build the synthesized envelope for an orphan row. */
|
|
242
|
+
export declare function buildOrphanDetail(input: BuildOrphanInput): ExtensionDetail;
|
|
243
|
+
/**
|
|
244
|
+
* List-mode meta string for one row. Returns `undefined` when the row has
|
|
245
|
+
* nothing surface-worthy beyond the type tag (which is always present).
|
|
246
|
+
*/
|
|
247
|
+
export declare function listMetaFor(detail: ExtensionDetail): string;
|
|
248
|
+
/**
|
|
249
|
+
* List-mode meta string for an orphan row. The type tag is replaced with
|
|
250
|
+
* the status word since there's no manifest to derive type from.
|
|
251
|
+
*/
|
|
252
|
+
export declare function orphanListMeta(fnCount: number, compCount: number): string;
|
|
253
|
+
/**
|
|
254
|
+
* `Next steps:` lines per status. Runnable shell commands come first,
|
|
255
|
+
* merchant-UI lines come second prefixed with `(merchant)` so an agent can
|
|
256
|
+
* filter. Empty array when there are no actionable next steps.
|
|
257
|
+
*/
|
|
258
|
+
export declare function nextStepLines(detail: ExtensionDetail): string[];
|
|
259
|
+
/**
|
|
260
|
+
* Section ordering for the list view. Apps render alphabetically; orphan
|
|
261
|
+
* rows land in a single trailing `<orphans>` group.
|
|
262
|
+
*/
|
|
263
|
+
export declare function listGroupForApp(slug: string): GroupInfo;
|
|
264
|
+
export declare function listGroupForOrphans(): GroupInfo;
|
|
265
|
+
/** True when a function/component "belongs to" an extension by direct match. */
|
|
266
|
+
export declare function functionMatchesExtension(fn: FunctionRecord, extId: string): boolean;
|
|
267
|
+
export declare function componentMatchesExtension(comp: ComponentRecord, extId: string): boolean;
|