@carthooks/arcubase-cli 0.1.25 → 0.1.26
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/bundle/arcubase-admin.mjs +505 -164
- package/bundle/arcubase.mjs +505 -164
- package/dist/generated/zod_registry.generated.d.ts.map +1 -1
- package/dist/generated/zod_registry.generated.js +3 -3
- package/dist/runtime/dev_sdk_gen.d.ts +7 -2
- package/dist/runtime/dev_sdk_gen.d.ts.map +1 -1
- package/dist/runtime/dev_sdk_gen.js +593 -8
- package/dist/runtime/execute.d.ts.map +1 -1
- package/dist/runtime/execute.js +99 -7
- package/package.json +1 -2
- package/sdk-dist/docs/runtime-reference/dev-sdk-gen.md +14 -10
- package/sdk-dist/generated/zod_registry.generated.ts +3 -3
- package/sdk-dist/types/common.ts +1 -0
- package/sdk-dist/types/ingress.ts +15 -0
- package/sdk-dist/types/user-action.ts +1 -0
- package/src/generated/zod_registry.generated.ts +3 -3
- package/src/runtime/dev_sdk_gen.ts +660 -14
- package/src/runtime/execute.ts +107 -6
- package/src/tests/dev_sdk_gen.test.ts +125 -36
- package/src/tests/help.test.ts +2 -1
- package/src/tests/zod_registry.test.ts +1 -0
package/bundle/arcubase.mjs
CHANGED
|
@@ -16529,10 +16529,10 @@ var PermitFieldPermitSchema = external_exports.lazy(() => external_exports.objec
|
|
|
16529
16529
|
var PermitPolicySchema = external_exports.lazy(() => external_exports.object({ "actions": external_exports.array(external_exports.string()).optional(), "all_fields_read": external_exports.boolean().optional(), "all_fields_write": external_exports.boolean().optional(), "condition": PermitConditionSetSchema.optional(), "description": external_exports.string().optional(), "fields": external_exports.array(PermitFieldPermitSchema).optional(), "id": external_exports.string().optional(), "key": external_exports.string().optional(), "name": external_exports.string().optional() }).strict());
|
|
16530
16530
|
var PermitUserScopeSchema = external_exports.lazy(() => external_exports.object({ "data": external_exports.object({ "name": external_exports.string().optional() }).strict().optional(), "id": external_exports.number().optional(), "type": external_exports.string().optional() }).strict());
|
|
16531
16531
|
var EntityIngressOptionsSchema = external_exports.lazy(() => external_exports.object({ "i18n_name": I18NTextSchema.optional(), "list_options": PermitEntityListOptionsSchema.optional(), "policy": PermitPolicySchema.optional(), "user_scope": external_exports.array(PermitUserScopeSchema).optional() }).strict());
|
|
16532
|
-
var AppIngressBulkApplyItemVOSchema = external_exports.lazy(() => external_exports.object({ "enabled": external_exports.boolean().optional(), "name": external_exports.string().optional(), "options": EntityIngressOptionsSchema.optional(), "rule_id": external_exports.number().optional(), "table_id": external_exports.number().optional(), "type": external_exports.string().optional() }).strict());
|
|
16532
|
+
var AppIngressBulkApplyItemVOSchema = external_exports.lazy(() => external_exports.object({ "enabled": external_exports.boolean().optional(), "key": external_exports.string().optional(), "name": external_exports.string().optional(), "options": EntityIngressOptionsSchema.optional(), "rule_id": external_exports.number().optional(), "table_id": external_exports.number().optional(), "type": external_exports.string().optional() }).strict());
|
|
16533
16533
|
var AppIngressBulkApplyReqVOSchema = external_exports.lazy(() => external_exports.object({ "dry_run": external_exports.boolean().optional(), "items": external_exports.array(AppIngressBulkApplyItemVOSchema).optional(), "mode": external_exports.string().optional() }).strict());
|
|
16534
|
-
var AppIngressCreateReqVOSchema = external_exports.lazy(() => external_exports.object({ "enabled": external_exports.boolean().optional(), "entity_id": external_exports.number().optional(), "icon_name": external_exports.string().optional(), "name": external_exports.string().optional(), "options": EntityIngressOptionsSchema.optional(), "type": external_exports.string().optional(), "url": external_exports.string().optional() }).strict());
|
|
16535
|
-
var AppIngressUpdateReqVOSchema = external_exports.lazy(() => external_exports.object({ "enabled": external_exports.boolean().optional(), "name": external_exports.string().optional(), "options": EntityIngressOptionsSchema.optional(), "update": external_exports.array(external_exports.string()).optional() }).strict());
|
|
16534
|
+
var AppIngressCreateReqVOSchema = external_exports.lazy(() => external_exports.object({ "enabled": external_exports.boolean().optional(), "entity_id": external_exports.number().optional(), "icon_name": external_exports.string().optional(), "key": external_exports.string().optional(), "name": external_exports.string().optional(), "options": EntityIngressOptionsSchema.optional(), "type": external_exports.string().optional(), "url": external_exports.string().optional() }).strict());
|
|
16535
|
+
var AppIngressUpdateReqVOSchema = external_exports.lazy(() => external_exports.object({ "enabled": external_exports.boolean().optional(), "key": external_exports.string().optional(), "name": external_exports.string().optional(), "options": EntityIngressOptionsSchema.optional(), "update": external_exports.array(external_exports.string()).optional() }).strict());
|
|
16536
16536
|
var WidgetsTypeCodeSchema = external_exports.lazy(() => external_exports.union([external_exports.literal(1), external_exports.literal(2)]));
|
|
16537
16537
|
var WidgetsTypeSchema = external_exports.lazy(() => external_exports.object({ "dashboard_id": external_exports.number().optional(), "ingress_id": external_exports.number().optional(), "type": WidgetsTypeCodeSchema.optional() }).strict());
|
|
16538
16538
|
var AppNewWidgetsReqVOSchema = external_exports.lazy(() => external_exports.object({ "options": WidgetsTypeSchema.optional(), "type": external_exports.string().optional() }).strict());
|
|
@@ -18689,15 +18689,8 @@ function renderCommandHelpExamples(scope, command) {
|
|
|
18689
18689
|
].join("\n").trimEnd();
|
|
18690
18690
|
}
|
|
18691
18691
|
|
|
18692
|
-
//
|
|
18693
|
-
var
|
|
18694
|
-
code;
|
|
18695
|
-
constructor(code, message) {
|
|
18696
|
-
super(message);
|
|
18697
|
-
this.code = code;
|
|
18698
|
-
this.name = "ArcubaseCodeGenError";
|
|
18699
|
-
}
|
|
18700
|
-
};
|
|
18692
|
+
// src/runtime/dev_sdk_gen.ts
|
|
18693
|
+
var identifierPattern = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
18701
18694
|
function isRecord3(value) {
|
|
18702
18695
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
18703
18696
|
}
|
|
@@ -18706,18 +18699,9 @@ function unwrapData(payload) {
|
|
|
18706
18699
|
}
|
|
18707
18700
|
function toPascalCase(value) {
|
|
18708
18701
|
const words = value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter(Boolean);
|
|
18709
|
-
if (words.length === 0)
|
|
18710
|
-
return "Entity";
|
|
18702
|
+
if (words.length === 0) return "Ingress";
|
|
18711
18703
|
return words.map((word) => `${word[0].toUpperCase()}${word.slice(1)}`).join("");
|
|
18712
18704
|
}
|
|
18713
|
-
function toCamelCase(value) {
|
|
18714
|
-
const pascal = toPascalCase(value);
|
|
18715
|
-
return `${pascal[0].toLowerCase()}${pascal.slice(1)}`;
|
|
18716
|
-
}
|
|
18717
|
-
function sanitizeEntityKey(entity) {
|
|
18718
|
-
const fallback = entity.sdkName || `entity_${entity.id}`;
|
|
18719
|
-
return toCamelCase(fallback) === "entity" ? `entity${entity.id}` : toCamelCase(fallback);
|
|
18720
|
-
}
|
|
18721
18705
|
function normalizeFieldType(type) {
|
|
18722
18706
|
switch (type) {
|
|
18723
18707
|
case "number":
|
|
@@ -18744,65 +18728,122 @@ function normalizeFieldType(type) {
|
|
|
18744
18728
|
return "string";
|
|
18745
18729
|
}
|
|
18746
18730
|
}
|
|
18747
|
-
function
|
|
18731
|
+
function normalizeField(field) {
|
|
18732
|
+
if (typeof field.id !== "number" || typeof field.label !== "string" || typeof field.type !== "string") {
|
|
18733
|
+
return void 0;
|
|
18734
|
+
}
|
|
18735
|
+
const children = Array.isArray(field.children) ? field.children.filter((child) => isRecord3(child)).map(normalizeField).filter((child) => Boolean(child)) : void 0;
|
|
18736
|
+
return {
|
|
18737
|
+
id: field.id,
|
|
18738
|
+
label: field.label,
|
|
18739
|
+
key: typeof field.key === "string" ? field.key.trim() : "",
|
|
18740
|
+
type: field.type,
|
|
18741
|
+
required: field.required === true,
|
|
18742
|
+
options: isRecord3(field.options) ? field.options : void 0,
|
|
18743
|
+
children
|
|
18744
|
+
};
|
|
18745
|
+
}
|
|
18746
|
+
function normalizeEntity(payload, appId) {
|
|
18747
|
+
if (!isRecord3(payload) || typeof payload.id !== "number" || typeof payload.name !== "string") {
|
|
18748
|
+
throw new CLIError("SDK_GEN_INVALID_SCHEMA", "sdk-gen expected ingress.entity with id and name", 2);
|
|
18749
|
+
}
|
|
18750
|
+
const fields = Array.isArray(payload.fields) ? payload.fields.filter((field) => isRecord3(field)).map(normalizeField).filter((field) => Boolean(field)) : [];
|
|
18751
|
+
return {
|
|
18752
|
+
appId,
|
|
18753
|
+
id: payload.id,
|
|
18754
|
+
name: payload.name,
|
|
18755
|
+
fields
|
|
18756
|
+
};
|
|
18757
|
+
}
|
|
18758
|
+
function normalizeActions(ingress) {
|
|
18759
|
+
const policy = isRecord3(ingress.policy) ? ingress.policy : isRecord3(ingress.options) && isRecord3(ingress.options.policy) ? ingress.options.policy : void 0;
|
|
18760
|
+
const source = Array.isArray(ingress.actions) ? ingress.actions : Array.isArray(policy?.actions) ? policy.actions : [];
|
|
18761
|
+
return source.filter((action) => typeof action === "string" && action.trim() !== "").map((action) => action.trim());
|
|
18762
|
+
}
|
|
18763
|
+
function normalizeIngresses(payload) {
|
|
18748
18764
|
const root = unwrapData(payload);
|
|
18749
18765
|
if (!isRecord3(root)) {
|
|
18750
|
-
throw new
|
|
18766
|
+
throw new CLIError("SDK_GEN_INVALID_SCHEMA", "sdk-gen expected an app ingress schema object", 2);
|
|
18751
18767
|
}
|
|
18752
18768
|
const appId = typeof root.id === "number" ? root.id : typeof root.app_id === "number" ? root.app_id : void 0;
|
|
18753
18769
|
if (!appId) {
|
|
18754
|
-
throw new
|
|
18770
|
+
throw new CLIError("SDK_GEN_APP_ID_REQUIRED", "sdk-gen expected app id in schema payload", 2);
|
|
18755
18771
|
}
|
|
18756
|
-
const
|
|
18757
|
-
|
|
18758
|
-
|
|
18759
|
-
|
|
18760
|
-
|
|
18761
|
-
|
|
18762
|
-
|
|
18763
|
-
|
|
18764
|
-
|
|
18765
|
-
})) : [];
|
|
18766
|
-
const entity = {
|
|
18772
|
+
const sourceIngresses = Array.isArray(root.ingresses) ? root.ingresses : [];
|
|
18773
|
+
if (sourceIngresses.length === 0) {
|
|
18774
|
+
throw new CLIError("SDK_GEN_NO_INGRESSES", "sdk-gen could not find ingresses in schema payload", 2);
|
|
18775
|
+
}
|
|
18776
|
+
const ingresses = sourceIngresses.filter((item) => isRecord3(item)).map((item) => {
|
|
18777
|
+
const entity = normalizeEntity(item.entity, appId);
|
|
18778
|
+
const options = isRecord3(item.options) ? item.options : {};
|
|
18779
|
+
const hashId = typeof item.hash_id === "string" ? item.hash_id.trim() : typeof item.hashId === "string" ? item.hashId.trim() : "";
|
|
18780
|
+
return {
|
|
18767
18781
|
appId,
|
|
18768
|
-
id: item.id,
|
|
18769
|
-
|
|
18770
|
-
|
|
18771
|
-
|
|
18782
|
+
id: typeof item.id === "number" ? item.id : void 0,
|
|
18783
|
+
hashId,
|
|
18784
|
+
key: typeof item.key === "string" ? item.key.trim() : "",
|
|
18785
|
+
name: typeof item.name === "string" && item.name.trim() ? item.name.trim() : String(item.key ?? ""),
|
|
18786
|
+
actions: normalizeActions(item),
|
|
18787
|
+
entity,
|
|
18788
|
+
listOptions: isRecord3(options.list_options) ? options.list_options : isRecord3(item.list_options) ? item.list_options : void 0
|
|
18772
18789
|
};
|
|
18773
|
-
entity.sdkName = sanitizeEntityKey(entity);
|
|
18774
|
-
return entity;
|
|
18775
18790
|
});
|
|
18776
|
-
|
|
18777
|
-
|
|
18791
|
+
validateIngresses(ingresses);
|
|
18792
|
+
return ingresses;
|
|
18793
|
+
}
|
|
18794
|
+
function walkFields(fields, visit) {
|
|
18795
|
+
for (const field of fields) {
|
|
18796
|
+
visit(field);
|
|
18797
|
+
if (field.children && field.children.length > 0) {
|
|
18798
|
+
walkFields(field.children, visit);
|
|
18799
|
+
}
|
|
18778
18800
|
}
|
|
18779
|
-
validateEntities(entities);
|
|
18780
|
-
return entities;
|
|
18781
18801
|
}
|
|
18782
|
-
function
|
|
18783
|
-
const
|
|
18784
|
-
|
|
18785
|
-
|
|
18786
|
-
|
|
18802
|
+
function allFields(entity) {
|
|
18803
|
+
const out = [];
|
|
18804
|
+
walkFields(entity.fields, (field) => out.push(field));
|
|
18805
|
+
return out;
|
|
18806
|
+
}
|
|
18807
|
+
function validateIngresses(ingresses) {
|
|
18808
|
+
const ingressKeys = /* @__PURE__ */ new Set();
|
|
18809
|
+
for (const ingress of ingresses) {
|
|
18810
|
+
if (!ingress.key) {
|
|
18811
|
+
throw new CLIError("SDK_GEN_INGRESS_KEY_REQUIRED", `ingress.key is required for ${ingress.name || "unnamed ingress"}`, 2);
|
|
18812
|
+
}
|
|
18813
|
+
if (!identifierPattern.test(ingress.key)) {
|
|
18814
|
+
throw new CLIError("SDK_GEN_INVALID_INGRESS_KEY", `ingress.key must be a TypeScript identifier: ${ingress.key}`, 2);
|
|
18815
|
+
}
|
|
18816
|
+
if (ingressKeys.has(ingress.key)) {
|
|
18817
|
+
throw new CLIError("SDK_GEN_DUPLICATE_INGRESS_KEY", `duplicate ingress.key: ${ingress.key}`, 2);
|
|
18818
|
+
}
|
|
18819
|
+
ingressKeys.add(ingress.key);
|
|
18820
|
+
if (!ingress.hashId) {
|
|
18821
|
+
throw new CLIError("SDK_GEN_INGRESS_HASH_ID_REQUIRED", `ingress.hash_id is required for ${ingress.key}`, 2);
|
|
18787
18822
|
}
|
|
18788
|
-
entityKeys.add(entity.sdkName);
|
|
18789
18823
|
const fieldKeys = /* @__PURE__ */ new Set();
|
|
18790
|
-
for (const field of entity
|
|
18824
|
+
for (const field of allFields(ingress.entity)) {
|
|
18791
18825
|
if (!field.key) {
|
|
18792
|
-
throw new
|
|
18826
|
+
throw new CLIError("SDK_GEN_FIELD_KEY_REQUIRED", `field.key is required for ${ingress.entity.name}.${field.label} (${field.id})`, 2);
|
|
18793
18827
|
}
|
|
18794
|
-
if (
|
|
18795
|
-
throw new
|
|
18828
|
+
if (!identifierPattern.test(field.key)) {
|
|
18829
|
+
throw new CLIError("SDK_GEN_INVALID_FIELD_KEY", `field.key must be a TypeScript identifier: ${ingress.entity.name}.${field.key}`, 2);
|
|
18796
18830
|
}
|
|
18797
18831
|
if (fieldKeys.has(field.key)) {
|
|
18798
|
-
throw new
|
|
18832
|
+
throw new CLIError("SDK_GEN_DUPLICATE_FIELD_KEY", `duplicate field.key in ${ingress.entity.name}: ${field.key}`, 2);
|
|
18799
18833
|
}
|
|
18800
18834
|
fieldKeys.add(field.key);
|
|
18801
18835
|
}
|
|
18802
18836
|
}
|
|
18803
18837
|
}
|
|
18838
|
+
function hasAction(ingress, action) {
|
|
18839
|
+
return ingress.actions.includes(action);
|
|
18840
|
+
}
|
|
18841
|
+
function methodLines(lines) {
|
|
18842
|
+
return lines.length > 0 ? `${lines.join("\n\n")}
|
|
18843
|
+
` : "";
|
|
18844
|
+
}
|
|
18804
18845
|
function emitRuntime() {
|
|
18805
|
-
return `export type
|
|
18846
|
+
return `export type ArcubaseClientConfig = {
|
|
18806
18847
|
baseURL: string
|
|
18807
18848
|
accessToken?: string
|
|
18808
18849
|
refreshToken?: string
|
|
@@ -18816,7 +18857,6 @@ export type ArcubaseRow<TFields> = {
|
|
|
18816
18857
|
}
|
|
18817
18858
|
|
|
18818
18859
|
export type ArcubaseRuntime = {
|
|
18819
|
-
config: ArcubaseSdkConfig
|
|
18820
18860
|
request<T>(method: string, endpoint: string, body?: unknown): Promise<T>
|
|
18821
18861
|
}
|
|
18822
18862
|
|
|
@@ -18825,7 +18865,7 @@ type ArcubaseEnvelope<T> = {
|
|
|
18825
18865
|
error?: { message?: string; key?: string; type?: string }
|
|
18826
18866
|
}
|
|
18827
18867
|
|
|
18828
|
-
export function createRuntime(config:
|
|
18868
|
+
export function createRuntime(config: ArcubaseClientConfig): ArcubaseRuntime {
|
|
18829
18869
|
let accessToken = config.accessToken ?? ''
|
|
18830
18870
|
let refreshToken = config.refreshToken ?? ''
|
|
18831
18871
|
|
|
@@ -18836,6 +18876,7 @@ export function createRuntime(config: ArcubaseSdkConfig): ArcubaseRuntime {
|
|
|
18836
18876
|
headers: {
|
|
18837
18877
|
'Content-Type': 'application/json',
|
|
18838
18878
|
...(accessToken ? { Authorization: \`Bearer \${accessToken}\` } : {}),
|
|
18879
|
+
...(refreshToken ? { 'X-Arcubase-Refresh-Token': refreshToken } : {}),
|
|
18839
18880
|
},
|
|
18840
18881
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
18841
18882
|
})
|
|
@@ -18846,21 +18887,72 @@ export function createRuntime(config: ArcubaseSdkConfig): ArcubaseRuntime {
|
|
|
18846
18887
|
return payload?.data as T
|
|
18847
18888
|
}
|
|
18848
18889
|
|
|
18849
|
-
return {
|
|
18850
|
-
|
|
18851
|
-
|
|
18890
|
+
return { request }
|
|
18891
|
+
}
|
|
18892
|
+
|
|
18893
|
+
export function toArcubaseFields<T extends Record<string, any>>(fields: Partial<T>, fieldIds: Record<string, number>): Record<string, any> {
|
|
18894
|
+
const out: Record<string, any> = {}
|
|
18895
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
18896
|
+
if (value === undefined) continue
|
|
18897
|
+
const fieldId = fieldIds[key]
|
|
18898
|
+
if (!fieldId) {
|
|
18899
|
+
throw new Error(\`Unknown Arcubase field: \${key}\`)
|
|
18900
|
+
}
|
|
18901
|
+
out[String(fieldId)] = value
|
|
18902
|
+
}
|
|
18903
|
+
return out
|
|
18904
|
+
}
|
|
18905
|
+
|
|
18906
|
+
export function fromArcubaseFields<T>(raw: Record<string, any> | undefined, fieldIds: Record<string, number>): T {
|
|
18907
|
+
const out: Record<string, any> = {}
|
|
18908
|
+
const source = raw ?? {}
|
|
18909
|
+
for (const [key, fieldId] of Object.entries(fieldIds)) {
|
|
18910
|
+
if (String(fieldId) in source) {
|
|
18911
|
+
out[key] = source[String(fieldId)]
|
|
18912
|
+
}
|
|
18852
18913
|
}
|
|
18914
|
+
return out as T
|
|
18915
|
+
}
|
|
18916
|
+
|
|
18917
|
+
export function toArcubaseColumn(column: string, fieldIds: Record<string, number>): string {
|
|
18918
|
+
return fieldIds[column] ? \`:\${fieldIds[column]}\` : column
|
|
18919
|
+
}
|
|
18920
|
+
|
|
18921
|
+
export function mapArcubaseCondition(value: unknown, fieldIds: Record<string, number>): unknown {
|
|
18922
|
+
if (Array.isArray(value)) {
|
|
18923
|
+
return value.map((item) => mapArcubaseCondition(item, fieldIds))
|
|
18924
|
+
}
|
|
18925
|
+
if (!value || typeof value !== 'object') {
|
|
18926
|
+
return value
|
|
18927
|
+
}
|
|
18928
|
+
const out: Record<string, unknown> = {}
|
|
18929
|
+
for (const [key, child] of Object.entries(value as Record<string, unknown>)) {
|
|
18930
|
+
if (key === 'column' && typeof child === 'string') {
|
|
18931
|
+
out[key] = toArcubaseColumn(child, fieldIds)
|
|
18932
|
+
} else {
|
|
18933
|
+
const mappedKey = fieldIds[key] ? \`:\${fieldIds[key]}\` : key
|
|
18934
|
+
out[mappedKey] = mapArcubaseCondition(child, fieldIds)
|
|
18935
|
+
}
|
|
18936
|
+
}
|
|
18937
|
+
return out
|
|
18938
|
+
}
|
|
18939
|
+
|
|
18940
|
+
export function mapArcubaseSorts<T extends { column: string; order?: string }>(sorts: T[] | undefined, fieldIds: Record<string, number>): T[] | undefined {
|
|
18941
|
+
return sorts?.map((sort) => ({ ...sort, column: toArcubaseColumn(sort.column, fieldIds) }))
|
|
18853
18942
|
}
|
|
18854
18943
|
`;
|
|
18855
18944
|
}
|
|
18856
|
-
function emitSchema(
|
|
18945
|
+
function emitSchema(ingresses) {
|
|
18857
18946
|
const schema = {
|
|
18858
|
-
|
|
18859
|
-
|
|
18947
|
+
ingresses: Object.fromEntries(ingresses.map((ingress) => [
|
|
18948
|
+
ingress.key,
|
|
18860
18949
|
{
|
|
18861
|
-
|
|
18862
|
-
|
|
18863
|
-
|
|
18950
|
+
appId: ingress.appId,
|
|
18951
|
+
ingressId: ingress.hashId,
|
|
18952
|
+
entityId: ingress.entity.id,
|
|
18953
|
+
name: ingress.name,
|
|
18954
|
+
actions: ingress.actions,
|
|
18955
|
+
fields: Object.fromEntries(allFields(ingress.entity).map((field) => [
|
|
18864
18956
|
field.key,
|
|
18865
18957
|
{
|
|
18866
18958
|
fieldId: field.id,
|
|
@@ -18874,17 +18966,177 @@ function emitSchema(entities) {
|
|
|
18874
18966
|
};
|
|
18875
18967
|
return `export const arcubaseSchema = ${JSON.stringify(schema, null, 2)} as const
|
|
18876
18968
|
|
|
18877
|
-
export type
|
|
18969
|
+
export type ArcubaseIngressKey = keyof typeof arcubaseSchema.ingresses
|
|
18878
18970
|
`;
|
|
18879
18971
|
}
|
|
18880
|
-
function
|
|
18881
|
-
const typeName = toPascalCase(
|
|
18882
|
-
const
|
|
18972
|
+
function emitIngress(ingress) {
|
|
18973
|
+
const typeName = toPascalCase(ingress.key);
|
|
18974
|
+
const fields = allFields(ingress.entity);
|
|
18975
|
+
const fieldLines = fields.map((field) => {
|
|
18883
18976
|
const optional2 = field.required ? "" : "?";
|
|
18884
18977
|
return ` ${field.key}${optional2}: ${normalizeFieldType(field.type)}`;
|
|
18885
18978
|
});
|
|
18886
|
-
const fieldMapLines =
|
|
18887
|
-
|
|
18979
|
+
const fieldMapLines = fields.map((field) => ` ${field.key}: ${field.id},`);
|
|
18980
|
+
const methodChunks = [];
|
|
18981
|
+
if (hasAction(ingress, "view")) {
|
|
18982
|
+
methodChunks.push(` async query(params: ${typeName}QueryParams = {}): Promise<{ items: ArcubaseRow<${typeName}Fields>[]; total?: number; offset?: number; limit?: number }> {
|
|
18983
|
+
const data = await runtime.request<{ items?: Record<string, any>[]; total?: number; offset?: number; limit?: number }>('POST', \`/entity/${ingress.appId}/${ingress.entity.id}/data-query\`, {
|
|
18984
|
+
...params,
|
|
18985
|
+
policy_id: ingressId,
|
|
18986
|
+
search: mapArcubaseCondition(params.search, fieldIds),
|
|
18987
|
+
sorts: mapArcubaseSorts(params.sorts, fieldIds),
|
|
18988
|
+
})
|
|
18989
|
+
return {
|
|
18990
|
+
items: (data.items ?? []).map(mapRow),
|
|
18991
|
+
total: data.total,
|
|
18992
|
+
offset: data.offset,
|
|
18993
|
+
limit: data.limit,
|
|
18994
|
+
}
|
|
18995
|
+
},
|
|
18996
|
+
|
|
18997
|
+
async get(rowId: string | number): Promise<ArcubaseRow<${typeName}Fields>> {
|
|
18998
|
+
const data = await runtime.request<{ record?: Record<string, any> }>('GET', \`/entity/${ingress.appId}/${ingress.entity.id}/row/\${rowId}?policy_id=\${encodeURIComponent(ingressId)}\`)
|
|
18999
|
+
return mapRow(data.record ?? {})
|
|
19000
|
+
},`);
|
|
19001
|
+
}
|
|
19002
|
+
if (hasAction(ingress, "add")) {
|
|
19003
|
+
methodChunks.push(` async prepareCreate(): Promise<Record<string, any>> {
|
|
19004
|
+
return runtime.request<Record<string, any>>('POST', \`/entity/${ingress.appId}/${ingress.entity.id}/prepare\`, {
|
|
19005
|
+
policy_id: ingressId,
|
|
19006
|
+
})
|
|
19007
|
+
},
|
|
19008
|
+
|
|
19009
|
+
async create(fields: ${typeName}CreateInput): Promise<number> {
|
|
19010
|
+
return runtime.request<number>('POST', \`/entity/${ingress.appId}/${ingress.entity.id}/insert\`, {
|
|
19011
|
+
fields: toArcubaseFields(fields, fieldIds),
|
|
19012
|
+
policy_id: ingressId,
|
|
19013
|
+
})
|
|
19014
|
+
},`);
|
|
19015
|
+
}
|
|
19016
|
+
if (hasAction(ingress, "edit")) {
|
|
19017
|
+
methodChunks.push(` async update(rowId: string | number, fields: ${typeName}UpdateInput): Promise<boolean> {
|
|
19018
|
+
const data = toArcubaseFields(fields, fieldIds)
|
|
19019
|
+
return runtime.request<boolean>('PUT', \`/entity/${ingress.appId}/${ingress.entity.id}/row/\${rowId}\`, {
|
|
19020
|
+
data,
|
|
19021
|
+
changed_fields: Object.keys(data).map((fieldId) => Number(fieldId)),
|
|
19022
|
+
policy_id: ingressId,
|
|
19023
|
+
})
|
|
19024
|
+
},`);
|
|
19025
|
+
}
|
|
19026
|
+
if (hasAction(ingress, "delete")) {
|
|
19027
|
+
methodChunks.push(` async delete(selection: ${typeName}Selection): Promise<{ deleted?: number } | boolean> {
|
|
19028
|
+
return runtime.request<{ deleted?: number } | boolean>('POST', \`/entity/${ingress.appId}/${ingress.entity.id}/delete-item\`, {
|
|
19029
|
+
policy_id: ingressId,
|
|
19030
|
+
selection: toArcubaseSelection(selection),
|
|
19031
|
+
})
|
|
19032
|
+
},`);
|
|
19033
|
+
}
|
|
19034
|
+
if (hasAction(ingress, "bulk_update")) {
|
|
19035
|
+
methodChunks.push(` async bulkUpdate(selection: ${typeName}Selection, fields: ${typeName}BulkUpdateField[]): Promise<{ updated: number }> {
|
|
19036
|
+
return runtime.request<{ updated: number }>('POST', \`/entity/${ingress.appId}/${ingress.entity.id}/bulk-update\`, {
|
|
19037
|
+
policy_id: ingressId,
|
|
19038
|
+
selection: toArcubaseSelection(selection),
|
|
19039
|
+
fields: fields.map((field) => ({
|
|
19040
|
+
id: fieldIds[field.key],
|
|
19041
|
+
action: field.action,
|
|
19042
|
+
})),
|
|
19043
|
+
})
|
|
19044
|
+
},
|
|
19045
|
+
|
|
19046
|
+
async saveBulkUpdate(input: { name: string; subject: string; fields: ${typeName}BulkUpdateField[] }): Promise<string> {
|
|
19047
|
+
return runtime.request<string>('POST', \`/entity/${ingress.appId}/${ingress.entity.id}/bulk-update-save-as\`, {
|
|
19048
|
+
name: input.name,
|
|
19049
|
+
subject: input.subject,
|
|
19050
|
+
fields: input.fields.map((field) => ({
|
|
19051
|
+
id: fieldIds[field.key],
|
|
19052
|
+
action: field.action,
|
|
19053
|
+
})),
|
|
19054
|
+
})
|
|
19055
|
+
},`);
|
|
19056
|
+
}
|
|
19057
|
+
if (hasAction(ingress, "export")) {
|
|
19058
|
+
methodChunks.push(` async exportRows(options: ${typeName}ExportOptions = {}): Promise<{ task_id: string }> {
|
|
19059
|
+
const fieldKeys = options.fields ?? Object.keys(fieldIds) as ${typeName}FieldKey[]
|
|
19060
|
+
return runtime.request<{ task_id: string }>('POST', \`/entity/${ingress.appId}/${ingress.entity.id}/export\`, {
|
|
19061
|
+
policy_id: ingressId,
|
|
19062
|
+
props: options.props ?? [],
|
|
19063
|
+
fields: fieldKeys.map((key) => fieldIds[key]),
|
|
19064
|
+
propnames: options.propNames ?? {},
|
|
19065
|
+
options: { has_id: options.hasIdField ?? false },
|
|
19066
|
+
})
|
|
19067
|
+
},
|
|
19068
|
+
|
|
19069
|
+
async exportTask(taskId: string): Promise<Record<string, any>> {
|
|
19070
|
+
return runtime.request<Record<string, any>>('GET', \`/export-task/\${taskId}\`)
|
|
19071
|
+
},`);
|
|
19072
|
+
}
|
|
19073
|
+
if (hasAction(ingress, "batch_print")) {
|
|
19074
|
+
methodChunks.push(` async batchPrint(selection: ${typeName}Selection, params: { limit?: number; offset?: number } = {}): Promise<{ items?: Record<string, any>[]; total?: number }> {
|
|
19075
|
+
return runtime.request<{ items?: Record<string, any>[]; total?: number }>('POST', \`/entity/${ingress.appId}/${ingress.entity.id}/selection-query/batch_print\`, {
|
|
19076
|
+
...params,
|
|
19077
|
+
policy_id: ingressId,
|
|
19078
|
+
selection: toArcubaseSelection(selection),
|
|
19079
|
+
})
|
|
19080
|
+
},`);
|
|
19081
|
+
}
|
|
19082
|
+
if (hasAction(ingress, "archive")) {
|
|
19083
|
+
methodChunks.push(` async archive(selection: ${typeName}Selection): Promise<{ archived?: number } | boolean> {
|
|
19084
|
+
return runtime.request<{ archived?: number } | boolean>('POST', \`/entity/${ingress.appId}/${ingress.entity.id}/selection-query/archive\`, {
|
|
19085
|
+
policy_id: ingressId,
|
|
19086
|
+
selection: toArcubaseSelection(selection),
|
|
19087
|
+
})
|
|
19088
|
+
},`);
|
|
19089
|
+
}
|
|
19090
|
+
if (hasAction(ingress, "tag_manage")) {
|
|
19091
|
+
methodChunks.push(` tags: {
|
|
19092
|
+
async list(): Promise<string[]> {
|
|
19093
|
+
return runtime.request<string[]>('GET', \`/user-action-entity/${ingress.appId}/${ingress.entity.id}/tag-names\`)
|
|
19094
|
+
},
|
|
19095
|
+
async add(selection: ${typeName}Selection, tagNames: string[]): Promise<Record<string, any>> {
|
|
19096
|
+
return runtime.request<Record<string, any>>('POST', \`/user-action-entity/${ingress.appId}/${ingress.entity.id}/batch-add-tags\`, {
|
|
19097
|
+
policy_id: ingressId,
|
|
19098
|
+
selection: toArcubaseSelection(selection),
|
|
19099
|
+
tag_names: tagNames,
|
|
19100
|
+
})
|
|
19101
|
+
},
|
|
19102
|
+
async remove(selection: ${typeName}Selection, tagNames: string[]): Promise<boolean> {
|
|
19103
|
+
return runtime.request<boolean>('POST', \`/user-action-entity/${ingress.appId}/${ingress.entity.id}/batch-remove-tags\`, {
|
|
19104
|
+
policy_id: ingressId,
|
|
19105
|
+
selection: toArcubaseSelection(selection),
|
|
19106
|
+
tag_names: tagNames,
|
|
19107
|
+
})
|
|
19108
|
+
},
|
|
19109
|
+
async selection(selection: ${typeName}Selection): Promise<string[]> {
|
|
19110
|
+
return runtime.request<string[]>('POST', \`/user-action-entity/${ingress.appId}/${ingress.entity.id}/get-selection-tags\`, {
|
|
19111
|
+
policy_id: ingressId,
|
|
19112
|
+
selection: toArcubaseSelection(selection),
|
|
19113
|
+
})
|
|
19114
|
+
},
|
|
19115
|
+
},`);
|
|
19116
|
+
}
|
|
19117
|
+
const customActions = ingress.actions.filter((action) => !["view", "add", "edit", "delete", "bulk_update", "export", "batch_print", "archive", "tag_manage"].includes(action));
|
|
19118
|
+
if (customActions.length > 0) {
|
|
19119
|
+
methodChunks.push(` async action(action: ${typeName}CustomAction, payload: Record<string, any> = {}): Promise<Record<string, any>> {
|
|
19120
|
+
if (!allowedActions.includes(action)) {
|
|
19121
|
+
throw new Error(\`Action is not allowed for ${ingress.key}: \${action}\`)
|
|
19122
|
+
}
|
|
19123
|
+
return runtime.request<Record<string, any>>('POST', \`/entity/${ingress.appId}/${ingress.entity.id}/selection-query/\${encodeURIComponent(action)}\`, {
|
|
19124
|
+
...payload,
|
|
19125
|
+
policy_id: ingressId,
|
|
19126
|
+
selection: payload.selection ? toArcubaseSelection(payload.selection) : undefined,
|
|
19127
|
+
})
|
|
19128
|
+
},`);
|
|
19129
|
+
}
|
|
19130
|
+
return `import {
|
|
19131
|
+
fromArcubaseFields,
|
|
19132
|
+
mapArcubaseCondition,
|
|
19133
|
+
mapArcubaseSorts,
|
|
19134
|
+
toArcubaseFields,
|
|
19135
|
+
type ArcubaseRow,
|
|
19136
|
+
type ArcubaseRuntime,
|
|
19137
|
+
} from '../runtime.js'
|
|
19138
|
+
|
|
19139
|
+
export type ${typeName}FieldKey = ${fields.map((field) => `'${field.key}'`).join(" | ")}
|
|
18888
19140
|
|
|
18889
19141
|
export type ${typeName}Fields = {
|
|
18890
19142
|
${fieldLines.join("\n")}
|
|
@@ -18892,126 +19144,127 @@ ${fieldLines.join("\n")}
|
|
|
18892
19144
|
|
|
18893
19145
|
export type ${typeName}CreateInput = ${typeName}Fields
|
|
18894
19146
|
export type ${typeName}UpdateInput = Partial<${typeName}Fields>
|
|
19147
|
+
export type ${typeName}Action = ${ingress.actions.map((action) => `'${action}'`).join(" | ") || "never"}
|
|
19148
|
+
export type ${typeName}CustomAction = ${customActions.map((action) => `'${action}'`).join(" | ") || "never"}
|
|
18895
19149
|
|
|
18896
|
-
|
|
18897
|
-
|
|
18898
|
-
|
|
18899
|
-
}
|
|
19150
|
+
export type ${typeName}Sort = {
|
|
19151
|
+
column: ${typeName}FieldKey | 'id' | 'created_at' | 'updated_at' | 'creator' | string
|
|
19152
|
+
order: 'asc' | 'desc'
|
|
19153
|
+
}
|
|
18900
19154
|
|
|
18901
|
-
|
|
18902
|
-
|
|
18903
|
-
|
|
18904
|
-
|
|
18905
|
-
|
|
18906
|
-
|
|
18907
|
-
|
|
18908
|
-
}
|
|
18909
|
-
out[String(fieldId)] = value
|
|
18910
|
-
}
|
|
18911
|
-
return out
|
|
19155
|
+
export type ${typeName}QueryParams = {
|
|
19156
|
+
limit?: number
|
|
19157
|
+
offset?: number
|
|
19158
|
+
search?: Record<string, any>
|
|
19159
|
+
sorts?: ${typeName}Sort[]
|
|
19160
|
+
view_mode?: 'normal' | 'deleted' | 'archived'
|
|
19161
|
+
withSubForm?: boolean
|
|
18912
19162
|
}
|
|
18913
19163
|
|
|
18914
|
-
|
|
18915
|
-
|
|
18916
|
-
|
|
18917
|
-
|
|
18918
|
-
|
|
18919
|
-
|
|
18920
|
-
|
|
18921
|
-
|
|
18922
|
-
|
|
19164
|
+
export type ${typeName}Selection = {
|
|
19165
|
+
type: 'ids' | 'condition'
|
|
19166
|
+
length?: number
|
|
19167
|
+
ids?: Array<number | string>
|
|
19168
|
+
condition?: Record<string, any>
|
|
19169
|
+
sorts?: ${typeName}Sort[]
|
|
19170
|
+
}
|
|
19171
|
+
|
|
19172
|
+
export type ${typeName}BulkUpdateField = {
|
|
19173
|
+
key: ${typeName}FieldKey
|
|
19174
|
+
action: Record<string, any>
|
|
18923
19175
|
}
|
|
18924
19176
|
|
|
19177
|
+
export type ${typeName}ExportOptions = {
|
|
19178
|
+
fields?: ${typeName}FieldKey[]
|
|
19179
|
+
props?: string[]
|
|
19180
|
+
propNames?: Record<string, string>
|
|
19181
|
+
hasIdField?: boolean
|
|
19182
|
+
}
|
|
19183
|
+
|
|
19184
|
+
const ingressId = ${JSON.stringify(ingress.hashId)}
|
|
19185
|
+
const fieldIds = {
|
|
19186
|
+
${fieldMapLines.join("\n")}
|
|
19187
|
+
} as const
|
|
19188
|
+
const allowedActions = ${JSON.stringify(customActions)} as const
|
|
19189
|
+
|
|
18925
19190
|
function mapRow(raw: Record<string, any>): ArcubaseRow<${typeName}Fields> {
|
|
18926
19191
|
return {
|
|
18927
19192
|
id: Number(raw.id),
|
|
18928
19193
|
title: typeof raw.title === 'string' ? raw.title : undefined,
|
|
18929
|
-
fields: fromArcubaseFields(raw.fields),
|
|
19194
|
+
fields: fromArcubaseFields<${typeName}Fields>(raw.fields, fieldIds),
|
|
18930
19195
|
raw,
|
|
18931
19196
|
}
|
|
18932
19197
|
}
|
|
18933
19198
|
|
|
18934
|
-
|
|
19199
|
+
function toArcubaseSelection(selection: ${typeName}Selection): Record<string, any> {
|
|
18935
19200
|
return {
|
|
18936
|
-
|
|
18937
|
-
|
|
18938
|
-
|
|
18939
|
-
})
|
|
18940
|
-
},
|
|
18941
|
-
|
|
18942
|
-
async update(rowId: string | number, fields: ${typeName}UpdateInput): Promise<boolean> {
|
|
18943
|
-
const data = toArcubaseFields(fields)
|
|
18944
|
-
return runtime.request<boolean>('PUT', \`/entity/${entity.appId}/\${entityId}/row/\${rowId}\`, {
|
|
18945
|
-
data,
|
|
18946
|
-
changed_fields: Object.keys(data).map((fieldId) => Number(fieldId)),
|
|
18947
|
-
})
|
|
18948
|
-
},
|
|
18949
|
-
|
|
18950
|
-
async get(rowId: string | number): Promise<ArcubaseRow<${typeName}Fields>> {
|
|
18951
|
-
const data = await runtime.request<{ record?: Record<string, any> }>('GET', \`/entity/${entity.appId}/\${entityId}/row/\${rowId}\`)
|
|
18952
|
-
return mapRow(data.record ?? {})
|
|
18953
|
-
},
|
|
18954
|
-
|
|
18955
|
-
async query(params: { limit?: number; offset?: number; search?: Record<string, any> } = {}): Promise<{ items: ArcubaseRow<${typeName}Fields>[]; total?: number }> {
|
|
18956
|
-
const data = await runtime.request<{ items?: Record<string, any>[]; total?: number }>('POST', \`/entity/${entity.appId}/\${entityId}/data-query\`, params)
|
|
18957
|
-
return {
|
|
18958
|
-
items: (data.items ?? []).map(mapRow),
|
|
18959
|
-
total: data.total,
|
|
18960
|
-
}
|
|
18961
|
-
},
|
|
19201
|
+
...selection,
|
|
19202
|
+
condition: mapArcubaseCondition(selection.condition ?? {}, fieldIds),
|
|
19203
|
+
sorts: mapArcubaseSorts(selection.sorts, fieldIds),
|
|
18962
19204
|
}
|
|
18963
19205
|
}
|
|
19206
|
+
|
|
19207
|
+
export function create${typeName}Ingress(runtime: ArcubaseRuntime) {
|
|
19208
|
+
return {
|
|
19209
|
+
${methodLines(methodChunks)} }
|
|
19210
|
+
}
|
|
18964
19211
|
`;
|
|
18965
19212
|
}
|
|
18966
|
-
function emitClient(
|
|
18967
|
-
const imports =
|
|
18968
|
-
const typeName = toPascalCase(
|
|
18969
|
-
return `import { create${typeName}
|
|
19213
|
+
function emitClient(ingresses) {
|
|
19214
|
+
const imports = ingresses.map((ingress) => {
|
|
19215
|
+
const typeName = toPascalCase(ingress.key);
|
|
19216
|
+
return `import { create${typeName}Ingress } from './ingresses/${ingress.key}.js'`;
|
|
18970
19217
|
});
|
|
18971
|
-
const
|
|
18972
|
-
|
|
19218
|
+
const mapLines = ingresses.map((ingress) => ` ${JSON.stringify(ingress.key)}: ReturnType<typeof create${toPascalCase(ingress.key)}Ingress>`);
|
|
19219
|
+
const cases = ingresses.map((ingress) => ` case ${JSON.stringify(ingress.key)}:
|
|
19220
|
+
return create${toPascalCase(ingress.key)}Ingress(runtime) as ArcubaseIngressMap[K]`);
|
|
19221
|
+
return `import { createRuntime, type ArcubaseClientConfig } from './runtime.js'
|
|
18973
19222
|
${imports.join("\n")}
|
|
18974
19223
|
|
|
18975
|
-
export
|
|
19224
|
+
export type ArcubaseIngressMap = {
|
|
19225
|
+
${mapLines.join("\n")}
|
|
19226
|
+
}
|
|
19227
|
+
|
|
19228
|
+
export type ArcubaseIngressKey = keyof ArcubaseIngressMap
|
|
19229
|
+
|
|
19230
|
+
export function createArcubaseClient(config: ArcubaseClientConfig) {
|
|
18976
19231
|
const runtime = createRuntime(config)
|
|
18977
19232
|
return {
|
|
18978
|
-
|
|
19233
|
+
ingress<K extends ArcubaseIngressKey>(key: K): ArcubaseIngressMap[K] {
|
|
19234
|
+
switch (key) {
|
|
19235
|
+
${cases.join("\n")}
|
|
19236
|
+
default:
|
|
19237
|
+
throw new Error(\`Unknown Arcubase ingress: \${String(key)}\`)
|
|
19238
|
+
}
|
|
19239
|
+
},
|
|
18979
19240
|
}
|
|
18980
19241
|
}
|
|
18981
19242
|
`;
|
|
18982
19243
|
}
|
|
18983
|
-
function emitIndex(
|
|
18984
|
-
return `export {
|
|
18985
|
-
export type {
|
|
19244
|
+
function emitIndex(ingresses) {
|
|
19245
|
+
return `export { createArcubaseClient } from './client.js'
|
|
19246
|
+
export type { ArcubaseClientConfig, ArcubaseRow } from './runtime.js'
|
|
18986
19247
|
export { arcubaseSchema } from './schema.js'
|
|
18987
|
-
|
|
19248
|
+
export type { ArcubaseIngressKey, ArcubaseIngressMap } from './client.js'
|
|
19249
|
+
${ingresses.map((ingress) => {
|
|
19250
|
+
const typeName = toPascalCase(ingress.key);
|
|
19251
|
+
return `export type { ${typeName}Fields, ${typeName}CreateInput, ${typeName}UpdateInput, ${typeName}FieldKey, ${typeName}Selection } from './ingresses/${ingress.key}.js'`;
|
|
19252
|
+
}).join("\n")}
|
|
18988
19253
|
`;
|
|
18989
19254
|
}
|
|
18990
19255
|
function generateArcubaseProjectSDK(payload) {
|
|
18991
|
-
const
|
|
19256
|
+
const ingresses = normalizeIngresses(payload);
|
|
18992
19257
|
return {
|
|
18993
19258
|
files: [
|
|
18994
19259
|
{ path: "runtime.ts", contents: emitRuntime() },
|
|
18995
|
-
{ path: "schema.ts", contents: emitSchema(
|
|
18996
|
-
...
|
|
18997
|
-
{ path: "client.ts", contents: emitClient(
|
|
18998
|
-
{ path: "index.ts", contents: emitIndex(
|
|
19260
|
+
{ path: "schema.ts", contents: emitSchema(ingresses) },
|
|
19261
|
+
...ingresses.map((ingress) => ({ path: `ingresses/${ingress.key}.ts`, contents: emitIngress(ingress) })),
|
|
19262
|
+
{ path: "client.ts", contents: emitClient(ingresses) },
|
|
19263
|
+
{ path: "index.ts", contents: emitIndex(ingresses) }
|
|
18999
19264
|
]
|
|
19000
19265
|
};
|
|
19001
19266
|
}
|
|
19002
19267
|
|
|
19003
|
-
// src/runtime/dev_sdk_gen.ts
|
|
19004
|
-
function generateArcubaseProjectSDK2(payload) {
|
|
19005
|
-
try {
|
|
19006
|
-
return generateArcubaseProjectSDK(payload);
|
|
19007
|
-
} catch (error51) {
|
|
19008
|
-
if (error51 instanceof ArcubaseCodeGenError) {
|
|
19009
|
-
throw new CLIError(error51.code, error51.message, 2);
|
|
19010
|
-
}
|
|
19011
|
-
throw error51;
|
|
19012
|
-
}
|
|
19013
|
-
}
|
|
19014
|
-
|
|
19015
19268
|
// src/runtime/execute.ts
|
|
19016
19269
|
function renderRootHelp(scope) {
|
|
19017
19270
|
const binary = scope === "admin" ? "arcubase-admin" : "arcubase";
|
|
@@ -19243,10 +19496,10 @@ function renderDevSDKGenHelp() {
|
|
|
19243
19496
|
" - arcubase-admin dev sdk-gen --app-id <app_id> --out src/arcubase",
|
|
19244
19497
|
"",
|
|
19245
19498
|
"requirements:",
|
|
19499
|
+
" - every generated ingress must have a configured ingress.key; sdk-gen fails fast when keys are missing",
|
|
19246
19500
|
" - every generated field must have a configured field.key; sdk-gen fails fast when keys are missing",
|
|
19247
|
-
" -
|
|
19248
|
-
|
|
19249
|
-
" - generated code accepts createArcubaseSdk({ baseURL, accessToken, refreshToken })"
|
|
19501
|
+
" - ingress.key and field.key values must be stable unique TypeScript identifiers",
|
|
19502
|
+
' - generated code accepts createArcubaseClient({ baseURL, accessToken, refreshToken }).ingress("<IngressKey>")'
|
|
19250
19503
|
].join("\n");
|
|
19251
19504
|
}
|
|
19252
19505
|
function resolveEndpoint(command, flags) {
|
|
@@ -20788,6 +21041,85 @@ function assertSDKGenFieldKeysPresent(entity) {
|
|
|
20788
21041
|
]
|
|
20789
21042
|
});
|
|
20790
21043
|
}
|
|
21044
|
+
function extractSDKGenIngressRefs(payload) {
|
|
21045
|
+
const data = unwrapResponseData(payload);
|
|
21046
|
+
const source = Array.isArray(data) ? data : isRecord4(data) && Array.isArray(data.list) ? data.list : [];
|
|
21047
|
+
return source.filter((item) => isRecord4(item));
|
|
21048
|
+
}
|
|
21049
|
+
function ingressKeyOf(ingress) {
|
|
21050
|
+
return typeof ingress.key === "string" ? ingress.key.trim() : "";
|
|
21051
|
+
}
|
|
21052
|
+
function ingressHashIDOf(ingress) {
|
|
21053
|
+
if (typeof ingress.hash_id === "string" && ingress.hash_id.trim()) {
|
|
21054
|
+
return ingress.hash_id.trim();
|
|
21055
|
+
}
|
|
21056
|
+
if (typeof ingress.hashId === "string" && ingress.hashId.trim()) {
|
|
21057
|
+
return ingress.hashId.trim();
|
|
21058
|
+
}
|
|
21059
|
+
return "";
|
|
21060
|
+
}
|
|
21061
|
+
function assertSDKGenIngressesReady(ingresses) {
|
|
21062
|
+
if (ingresses.length === 0) {
|
|
21063
|
+
throw new CLIError("SDK_GEN_NO_INGRESSES", "sdk-gen could not find ingress definitions for this app", 2, {
|
|
21064
|
+
operation: "dev sdk-gen",
|
|
21065
|
+
suggestions: ["create at least one access rule with a stable ingress.key before running sdk-gen"]
|
|
21066
|
+
});
|
|
21067
|
+
}
|
|
21068
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21069
|
+
const missingKey = [];
|
|
21070
|
+
const missingHash = [];
|
|
21071
|
+
const invalidKey = [];
|
|
21072
|
+
const duplicateKey = [];
|
|
21073
|
+
for (const ingress of ingresses) {
|
|
21074
|
+
const key = ingressKeyOf(ingress);
|
|
21075
|
+
if (!key) {
|
|
21076
|
+
missingKey.push(ingress);
|
|
21077
|
+
continue;
|
|
21078
|
+
}
|
|
21079
|
+
if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(key)) {
|
|
21080
|
+
invalidKey.push(key);
|
|
21081
|
+
}
|
|
21082
|
+
if (seen.has(key)) {
|
|
21083
|
+
duplicateKey.push(key);
|
|
21084
|
+
}
|
|
21085
|
+
seen.add(key);
|
|
21086
|
+
if (!ingressHashIDOf(ingress)) {
|
|
21087
|
+
missingHash.push(ingress);
|
|
21088
|
+
}
|
|
21089
|
+
}
|
|
21090
|
+
if (missingKey.length > 0) {
|
|
21091
|
+
const preview = missingKey.slice(0, 8).map((ingress) => String(ingress.name ?? ingress.id ?? "unnamed ingress"));
|
|
21092
|
+
const suffix = missingKey.length > preview.length ? `, and ${missingKey.length - preview.length} more` : "";
|
|
21093
|
+
throw new CLIError("SDK_GEN_INGRESS_KEY_REQUIRED", `missing ingress.key values: ${preview.join(", ")}${suffix}`, 2, {
|
|
21094
|
+
operation: "dev sdk-gen",
|
|
21095
|
+
issues: missingKey.map((ingress) => ({
|
|
21096
|
+
path: `ingress.${String(ingress.id ?? "unknown")}.key`,
|
|
21097
|
+
message: `ingress.key is required for ${String(ingress.name ?? ingress.id ?? "unnamed ingress")}`
|
|
21098
|
+
})),
|
|
21099
|
+
suggestions: [
|
|
21100
|
+
"set stable ingress.key values in Arcubase admin before running sdk-gen",
|
|
21101
|
+
"rerun arcubase-admin dev sdk-gen after ingress keys are complete"
|
|
21102
|
+
]
|
|
21103
|
+
});
|
|
21104
|
+
}
|
|
21105
|
+
if (invalidKey.length > 0) {
|
|
21106
|
+
throw new CLIError("SDK_GEN_INVALID_INGRESS_KEY", `invalid ingress.key values: ${invalidKey.join(", ")}`, 2, {
|
|
21107
|
+
operation: "dev sdk-gen",
|
|
21108
|
+
suggestions: ["use TypeScript identifier keys such as publicEntry or customerPortal"]
|
|
21109
|
+
});
|
|
21110
|
+
}
|
|
21111
|
+
if (duplicateKey.length > 0) {
|
|
21112
|
+
throw new CLIError("SDK_GEN_DUPLICATE_INGRESS_KEY", `duplicate ingress.key values: ${duplicateKey.join(", ")}`, 2, {
|
|
21113
|
+
operation: "dev sdk-gen"
|
|
21114
|
+
});
|
|
21115
|
+
}
|
|
21116
|
+
if (missingHash.length > 0) {
|
|
21117
|
+
throw new CLIError("SDK_GEN_INGRESS_HASH_ID_REQUIRED", "sdk-gen expected hash_id for every ingress definition", 2, {
|
|
21118
|
+
operation: "dev sdk-gen",
|
|
21119
|
+
suggestions: ["upgrade Arcubase server so admin access-rule list returns hash_id"]
|
|
21120
|
+
});
|
|
21121
|
+
}
|
|
21122
|
+
}
|
|
20791
21123
|
async function executeDevSDKGen(runtimeEnv, flags, fetchImpl) {
|
|
20792
21124
|
validateDevSDKGenFlags(flags);
|
|
20793
21125
|
const appId = flagValue(flags, "app-id");
|
|
@@ -20814,7 +21146,7 @@ async function executeDevSDKGen(runtimeEnv, flags, fetchImpl) {
|
|
|
20814
21146
|
if (entityRefs.length === 0) {
|
|
20815
21147
|
throw new CLIError("SDK_GEN_NO_ENTITIES", "sdk-gen could not find entities in app detail response", 2);
|
|
20816
21148
|
}
|
|
20817
|
-
const
|
|
21149
|
+
const ingresses = [];
|
|
20818
21150
|
for (const entityRef of entityRefs) {
|
|
20819
21151
|
const entityEndpoint = `/api/apps/${encodeURIComponent(appId)}/entity/${encodeURIComponent(entityRef.id)}`;
|
|
20820
21152
|
const entityDetail = await requestJSON(runtimeEnv, "GET", entityEndpoint, void 0, fetchImpl);
|
|
@@ -20823,12 +21155,21 @@ async function executeDevSDKGen(runtimeEnv, flags, fetchImpl) {
|
|
|
20823
21155
|
throw new CLIError("SDK_GEN_INVALID_SCHEMA", "sdk-gen expected entity detail response data", 2);
|
|
20824
21156
|
}
|
|
20825
21157
|
assertSDKGenFieldKeysPresent(entityPayload);
|
|
20826
|
-
|
|
20827
|
-
|
|
20828
|
-
|
|
21158
|
+
const ingressEndpoint = `/api/apps/${encodeURIComponent(appId)}/entity/${encodeURIComponent(entityRef.id)}/ingress`;
|
|
21159
|
+
const ingressList = await requestJSON(runtimeEnv, "GET", ingressEndpoint, void 0, fetchImpl);
|
|
21160
|
+
const entityIngresses = extractSDKGenIngressRefs(ingressList.data).map((ingress) => ({
|
|
21161
|
+
...ingress,
|
|
21162
|
+
hash_id: ingressHashIDOf(ingress),
|
|
21163
|
+
key: ingressKeyOf(ingress),
|
|
21164
|
+
entity: entityPayload
|
|
21165
|
+
}));
|
|
21166
|
+
ingresses.push(...entityIngresses);
|
|
21167
|
+
}
|
|
21168
|
+
assertSDKGenIngressesReady(ingresses);
|
|
21169
|
+
const generated = generateArcubaseProjectSDK({
|
|
20829
21170
|
id: resolveSDKGenAppId(appId, appPayload),
|
|
20830
21171
|
name: typeof appPayload.name === "string" ? appPayload.name : void 0,
|
|
20831
|
-
|
|
21172
|
+
ingresses
|
|
20832
21173
|
});
|
|
20833
21174
|
const absoluteOutDir = path2.resolve(process.cwd(), outDir);
|
|
20834
21175
|
for (const file2 of generated.files) {
|