@carthooks/arcubase-cli 0.1.24 → 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 +527 -183
- package/bundle/arcubase.mjs +527 -183
- 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.map +1 -1
- package/dist/runtime/dev_sdk_gen.js +423 -144
- package/dist/runtime/execute.d.ts.map +1 -1
- package/dist/runtime/execute.js +122 -48
- package/package.json +1 -1
- package/sdk-dist/docs/runtime-reference/dev-sdk-gen.md +27 -12
- 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 +453 -147
- package/src/runtime/execute.ts +128 -51
- package/src/tests/dev_sdk_gen.test.ts +134 -61
- package/src/tests/help.test.ts +2 -1
- package/src/tests/zod_registry.test.ts +1 -0
|
@@ -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());
|
|
@@ -18690,6 +18690,7 @@ function renderCommandHelpExamples(scope, command) {
|
|
|
18690
18690
|
}
|
|
18691
18691
|
|
|
18692
18692
|
// src/runtime/dev_sdk_gen.ts
|
|
18693
|
+
var identifierPattern = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
18693
18694
|
function isRecord3(value) {
|
|
18694
18695
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
18695
18696
|
}
|
|
@@ -18698,17 +18699,9 @@ function unwrapData(payload) {
|
|
|
18698
18699
|
}
|
|
18699
18700
|
function toPascalCase(value) {
|
|
18700
18701
|
const words = value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter(Boolean);
|
|
18701
|
-
if (words.length === 0) return "
|
|
18702
|
+
if (words.length === 0) return "Ingress";
|
|
18702
18703
|
return words.map((word) => `${word[0].toUpperCase()}${word.slice(1)}`).join("");
|
|
18703
18704
|
}
|
|
18704
|
-
function toCamelCase(value) {
|
|
18705
|
-
const pascal = toPascalCase(value);
|
|
18706
|
-
return `${pascal[0].toLowerCase()}${pascal.slice(1)}`;
|
|
18707
|
-
}
|
|
18708
|
-
function sanitizeEntityKey(entity) {
|
|
18709
|
-
const fallback = entity.sdkName || `entity_${entity.id}`;
|
|
18710
|
-
return toCamelCase(fallback) === "entity" ? `entity${entity.id}` : toCamelCase(fallback);
|
|
18711
|
-
}
|
|
18712
18705
|
function normalizeFieldType(type) {
|
|
18713
18706
|
switch (type) {
|
|
18714
18707
|
case "number":
|
|
@@ -18735,65 +18728,122 @@ function normalizeFieldType(type) {
|
|
|
18735
18728
|
return "string";
|
|
18736
18729
|
}
|
|
18737
18730
|
}
|
|
18738
|
-
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) {
|
|
18739
18764
|
const root = unwrapData(payload);
|
|
18740
18765
|
if (!isRecord3(root)) {
|
|
18741
|
-
throw new CLIError("SDK_GEN_INVALID_SCHEMA", "sdk-gen expected an app
|
|
18766
|
+
throw new CLIError("SDK_GEN_INVALID_SCHEMA", "sdk-gen expected an app ingress schema object", 2);
|
|
18742
18767
|
}
|
|
18743
18768
|
const appId = typeof root.id === "number" ? root.id : typeof root.app_id === "number" ? root.app_id : void 0;
|
|
18744
18769
|
if (!appId) {
|
|
18745
|
-
throw new CLIError("SDK_GEN_APP_ID_REQUIRED", "sdk-gen expected app id in
|
|
18770
|
+
throw new CLIError("SDK_GEN_APP_ID_REQUIRED", "sdk-gen expected app id in schema payload", 2);
|
|
18746
18771
|
}
|
|
18747
|
-
const
|
|
18748
|
-
|
|
18749
|
-
|
|
18750
|
-
|
|
18751
|
-
|
|
18752
|
-
|
|
18753
|
-
|
|
18754
|
-
|
|
18755
|
-
|
|
18756
|
-
})) : [];
|
|
18757
|
-
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 {
|
|
18758
18781
|
appId,
|
|
18759
|
-
id: item.id,
|
|
18760
|
-
|
|
18761
|
-
|
|
18762
|
-
|
|
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
|
|
18763
18789
|
};
|
|
18764
|
-
entity.sdkName = sanitizeEntityKey(entity);
|
|
18765
|
-
return entity;
|
|
18766
18790
|
});
|
|
18767
|
-
|
|
18768
|
-
|
|
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
|
+
}
|
|
18769
18800
|
}
|
|
18770
|
-
validateEntities(entities);
|
|
18771
|
-
return entities;
|
|
18772
18801
|
}
|
|
18773
|
-
function
|
|
18774
|
-
const
|
|
18775
|
-
|
|
18776
|
-
|
|
18777
|
-
|
|
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);
|
|
18778
18822
|
}
|
|
18779
|
-
entityKeys.add(entity.sdkName);
|
|
18780
18823
|
const fieldKeys = /* @__PURE__ */ new Set();
|
|
18781
|
-
for (const field of entity
|
|
18824
|
+
for (const field of allFields(ingress.entity)) {
|
|
18782
18825
|
if (!field.key) {
|
|
18783
|
-
throw new CLIError("SDK_GEN_FIELD_KEY_REQUIRED", `field.key is required for ${entity.name}.${field.label} (${field.id})`, 2);
|
|
18826
|
+
throw new CLIError("SDK_GEN_FIELD_KEY_REQUIRED", `field.key is required for ${ingress.entity.name}.${field.label} (${field.id})`, 2);
|
|
18784
18827
|
}
|
|
18785
|
-
if (
|
|
18786
|
-
throw new CLIError("SDK_GEN_INVALID_FIELD_KEY", `field.key must be a TypeScript identifier: ${entity.name}.${field.key}`, 2);
|
|
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);
|
|
18787
18830
|
}
|
|
18788
18831
|
if (fieldKeys.has(field.key)) {
|
|
18789
|
-
throw new CLIError("SDK_GEN_DUPLICATE_FIELD_KEY", `duplicate field.key in ${entity.name}: ${field.key}`, 2);
|
|
18832
|
+
throw new CLIError("SDK_GEN_DUPLICATE_FIELD_KEY", `duplicate field.key in ${ingress.entity.name}: ${field.key}`, 2);
|
|
18790
18833
|
}
|
|
18791
18834
|
fieldKeys.add(field.key);
|
|
18792
18835
|
}
|
|
18793
18836
|
}
|
|
18794
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
|
+
}
|
|
18795
18845
|
function emitRuntime() {
|
|
18796
|
-
return `export type
|
|
18846
|
+
return `export type ArcubaseClientConfig = {
|
|
18797
18847
|
baseURL: string
|
|
18798
18848
|
accessToken?: string
|
|
18799
18849
|
refreshToken?: string
|
|
@@ -18807,7 +18857,6 @@ export type ArcubaseRow<TFields> = {
|
|
|
18807
18857
|
}
|
|
18808
18858
|
|
|
18809
18859
|
export type ArcubaseRuntime = {
|
|
18810
|
-
config: ArcubaseSdkConfig
|
|
18811
18860
|
request<T>(method: string, endpoint: string, body?: unknown): Promise<T>
|
|
18812
18861
|
}
|
|
18813
18862
|
|
|
@@ -18816,7 +18865,7 @@ type ArcubaseEnvelope<T> = {
|
|
|
18816
18865
|
error?: { message?: string; key?: string; type?: string }
|
|
18817
18866
|
}
|
|
18818
18867
|
|
|
18819
|
-
export function createRuntime(config:
|
|
18868
|
+
export function createRuntime(config: ArcubaseClientConfig): ArcubaseRuntime {
|
|
18820
18869
|
let accessToken = config.accessToken ?? ''
|
|
18821
18870
|
let refreshToken = config.refreshToken ?? ''
|
|
18822
18871
|
|
|
@@ -18827,6 +18876,7 @@ export function createRuntime(config: ArcubaseSdkConfig): ArcubaseRuntime {
|
|
|
18827
18876
|
headers: {
|
|
18828
18877
|
'Content-Type': 'application/json',
|
|
18829
18878
|
...(accessToken ? { Authorization: \`Bearer \${accessToken}\` } : {}),
|
|
18879
|
+
...(refreshToken ? { 'X-Arcubase-Refresh-Token': refreshToken } : {}),
|
|
18830
18880
|
},
|
|
18831
18881
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
18832
18882
|
})
|
|
@@ -18837,21 +18887,72 @@ export function createRuntime(config: ArcubaseSdkConfig): ArcubaseRuntime {
|
|
|
18837
18887
|
return payload?.data as T
|
|
18838
18888
|
}
|
|
18839
18889
|
|
|
18840
|
-
return {
|
|
18841
|
-
|
|
18842
|
-
|
|
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
|
+
}
|
|
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
|
|
18843
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) }))
|
|
18844
18942
|
}
|
|
18845
18943
|
`;
|
|
18846
18944
|
}
|
|
18847
|
-
function emitSchema(
|
|
18945
|
+
function emitSchema(ingresses) {
|
|
18848
18946
|
const schema = {
|
|
18849
|
-
|
|
18850
|
-
|
|
18947
|
+
ingresses: Object.fromEntries(ingresses.map((ingress) => [
|
|
18948
|
+
ingress.key,
|
|
18851
18949
|
{
|
|
18852
|
-
|
|
18853
|
-
|
|
18854
|
-
|
|
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) => [
|
|
18855
18956
|
field.key,
|
|
18856
18957
|
{
|
|
18857
18958
|
fieldId: field.id,
|
|
@@ -18865,17 +18966,177 @@ function emitSchema(entities) {
|
|
|
18865
18966
|
};
|
|
18866
18967
|
return `export const arcubaseSchema = ${JSON.stringify(schema, null, 2)} as const
|
|
18867
18968
|
|
|
18868
|
-
export type
|
|
18969
|
+
export type ArcubaseIngressKey = keyof typeof arcubaseSchema.ingresses
|
|
18869
18970
|
`;
|
|
18870
18971
|
}
|
|
18871
|
-
function
|
|
18872
|
-
const typeName = toPascalCase(
|
|
18873
|
-
const
|
|
18972
|
+
function emitIngress(ingress) {
|
|
18973
|
+
const typeName = toPascalCase(ingress.key);
|
|
18974
|
+
const fields = allFields(ingress.entity);
|
|
18975
|
+
const fieldLines = fields.map((field) => {
|
|
18874
18976
|
const optional2 = field.required ? "" : "?";
|
|
18875
18977
|
return ` ${field.key}${optional2}: ${normalizeFieldType(field.type)}`;
|
|
18876
18978
|
});
|
|
18877
|
-
const fieldMapLines =
|
|
18878
|
-
|
|
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(" | ")}
|
|
18879
19140
|
|
|
18880
19141
|
export type ${typeName}Fields = {
|
|
18881
19142
|
${fieldLines.join("\n")}
|
|
@@ -18883,110 +19144,123 @@ ${fieldLines.join("\n")}
|
|
|
18883
19144
|
|
|
18884
19145
|
export type ${typeName}CreateInput = ${typeName}Fields
|
|
18885
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"}
|
|
18886
19149
|
|
|
18887
|
-
|
|
18888
|
-
|
|
18889
|
-
|
|
18890
|
-
}
|
|
19150
|
+
export type ${typeName}Sort = {
|
|
19151
|
+
column: ${typeName}FieldKey | 'id' | 'created_at' | 'updated_at' | 'creator' | string
|
|
19152
|
+
order: 'asc' | 'desc'
|
|
19153
|
+
}
|
|
18891
19154
|
|
|
18892
|
-
|
|
18893
|
-
|
|
18894
|
-
|
|
18895
|
-
|
|
18896
|
-
|
|
18897
|
-
|
|
18898
|
-
|
|
18899
|
-
}
|
|
18900
|
-
out[String(fieldId)] = value
|
|
18901
|
-
}
|
|
18902
|
-
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
|
|
18903
19162
|
}
|
|
18904
19163
|
|
|
18905
|
-
|
|
18906
|
-
|
|
18907
|
-
|
|
18908
|
-
|
|
18909
|
-
|
|
18910
|
-
|
|
18911
|
-
|
|
18912
|
-
|
|
18913
|
-
|
|
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>
|
|
19175
|
+
}
|
|
19176
|
+
|
|
19177
|
+
export type ${typeName}ExportOptions = {
|
|
19178
|
+
fields?: ${typeName}FieldKey[]
|
|
19179
|
+
props?: string[]
|
|
19180
|
+
propNames?: Record<string, string>
|
|
19181
|
+
hasIdField?: boolean
|
|
18914
19182
|
}
|
|
18915
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
|
+
|
|
18916
19190
|
function mapRow(raw: Record<string, any>): ArcubaseRow<${typeName}Fields> {
|
|
18917
19191
|
return {
|
|
18918
19192
|
id: Number(raw.id),
|
|
18919
19193
|
title: typeof raw.title === 'string' ? raw.title : undefined,
|
|
18920
|
-
fields: fromArcubaseFields(raw.fields),
|
|
19194
|
+
fields: fromArcubaseFields<${typeName}Fields>(raw.fields, fieldIds),
|
|
18921
19195
|
raw,
|
|
18922
19196
|
}
|
|
18923
19197
|
}
|
|
18924
19198
|
|
|
18925
|
-
|
|
19199
|
+
function toArcubaseSelection(selection: ${typeName}Selection): Record<string, any> {
|
|
18926
19200
|
return {
|
|
18927
|
-
|
|
18928
|
-
|
|
18929
|
-
|
|
18930
|
-
})
|
|
18931
|
-
},
|
|
18932
|
-
|
|
18933
|
-
async update(rowId: string | number, fields: ${typeName}UpdateInput): Promise<boolean> {
|
|
18934
|
-
const data = toArcubaseFields(fields)
|
|
18935
|
-
return runtime.request<boolean>('PUT', \`/entity/${entity.appId}/\${entityId}/row/\${rowId}\`, {
|
|
18936
|
-
data,
|
|
18937
|
-
changed_fields: Object.keys(data).map((fieldId) => Number(fieldId)),
|
|
18938
|
-
})
|
|
18939
|
-
},
|
|
18940
|
-
|
|
18941
|
-
async get(rowId: string | number): Promise<ArcubaseRow<${typeName}Fields>> {
|
|
18942
|
-
const data = await runtime.request<{ record?: Record<string, any> }>('GET', \`/entity/${entity.appId}/\${entityId}/row/\${rowId}\`)
|
|
18943
|
-
return mapRow(data.record ?? {})
|
|
18944
|
-
},
|
|
18945
|
-
|
|
18946
|
-
async query(params: { limit?: number; offset?: number; search?: Record<string, any> } = {}): Promise<{ items: ArcubaseRow<${typeName}Fields>[]; total?: number }> {
|
|
18947
|
-
const data = await runtime.request<{ items?: Record<string, any>[]; total?: number }>('POST', \`/entity/${entity.appId}/\${entityId}/data-query\`, params)
|
|
18948
|
-
return {
|
|
18949
|
-
items: (data.items ?? []).map(mapRow),
|
|
18950
|
-
total: data.total,
|
|
18951
|
-
}
|
|
18952
|
-
},
|
|
19201
|
+
...selection,
|
|
19202
|
+
condition: mapArcubaseCondition(selection.condition ?? {}, fieldIds),
|
|
19203
|
+
sorts: mapArcubaseSorts(selection.sorts, fieldIds),
|
|
18953
19204
|
}
|
|
18954
19205
|
}
|
|
19206
|
+
|
|
19207
|
+
export function create${typeName}Ingress(runtime: ArcubaseRuntime) {
|
|
19208
|
+
return {
|
|
19209
|
+
${methodLines(methodChunks)} }
|
|
19210
|
+
}
|
|
18955
19211
|
`;
|
|
18956
19212
|
}
|
|
18957
|
-
function emitClient(
|
|
18958
|
-
const imports =
|
|
18959
|
-
const typeName = toPascalCase(
|
|
18960
|
-
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'`;
|
|
18961
19217
|
});
|
|
18962
|
-
const
|
|
18963
|
-
|
|
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'
|
|
18964
19222
|
${imports.join("\n")}
|
|
18965
19223
|
|
|
18966
|
-
export
|
|
19224
|
+
export type ArcubaseIngressMap = {
|
|
19225
|
+
${mapLines.join("\n")}
|
|
19226
|
+
}
|
|
19227
|
+
|
|
19228
|
+
export type ArcubaseIngressKey = keyof ArcubaseIngressMap
|
|
19229
|
+
|
|
19230
|
+
export function createArcubaseClient(config: ArcubaseClientConfig) {
|
|
18967
19231
|
const runtime = createRuntime(config)
|
|
18968
19232
|
return {
|
|
18969
|
-
|
|
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
|
+
},
|
|
18970
19240
|
}
|
|
18971
19241
|
}
|
|
18972
19242
|
`;
|
|
18973
19243
|
}
|
|
18974
|
-
function emitIndex(
|
|
18975
|
-
return `export {
|
|
18976
|
-
export type {
|
|
19244
|
+
function emitIndex(ingresses) {
|
|
19245
|
+
return `export { createArcubaseClient } from './client.js'
|
|
19246
|
+
export type { ArcubaseClientConfig, ArcubaseRow } from './runtime.js'
|
|
18977
19247
|
export { arcubaseSchema } from './schema.js'
|
|
18978
|
-
|
|
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")}
|
|
18979
19253
|
`;
|
|
18980
19254
|
}
|
|
18981
19255
|
function generateArcubaseProjectSDK(payload) {
|
|
18982
|
-
const
|
|
19256
|
+
const ingresses = normalizeIngresses(payload);
|
|
18983
19257
|
return {
|
|
18984
19258
|
files: [
|
|
18985
19259
|
{ path: "runtime.ts", contents: emitRuntime() },
|
|
18986
|
-
{ path: "schema.ts", contents: emitSchema(
|
|
18987
|
-
...
|
|
18988
|
-
{ path: "client.ts", contents: emitClient(
|
|
18989
|
-
{ 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) }
|
|
18990
19264
|
]
|
|
18991
19265
|
};
|
|
18992
19266
|
}
|
|
@@ -19222,10 +19496,10 @@ function renderDevSDKGenHelp() {
|
|
|
19222
19496
|
" - arcubase-admin dev sdk-gen --app-id <app_id> --out src/arcubase",
|
|
19223
19497
|
"",
|
|
19224
19498
|
"requirements:",
|
|
19225
|
-
" -
|
|
19226
|
-
" -
|
|
19227
|
-
" -
|
|
19228
|
-
|
|
19499
|
+
" - every generated ingress must have a configured ingress.key; sdk-gen fails fast when keys are missing",
|
|
19500
|
+
" - every generated field must have a configured field.key; sdk-gen fails fast when keys are missing",
|
|
19501
|
+
" - ingress.key and field.key values must be stable unique TypeScript identifiers",
|
|
19502
|
+
' - generated code accepts createArcubaseClient({ baseURL, accessToken, refreshToken }).ingress("<IngressKey>")'
|
|
19229
19503
|
].join("\n");
|
|
19230
19504
|
}
|
|
19231
19505
|
function resolveEndpoint(command, flags) {
|
|
@@ -20735,45 +21009,116 @@ function walkSDKGenFields(fields, visit) {
|
|
|
20735
21009
|
walkSDKGenFields(field.children, visit);
|
|
20736
21010
|
}
|
|
20737
21011
|
}
|
|
20738
|
-
function
|
|
20739
|
-
const
|
|
21012
|
+
function assertSDKGenFieldKeysPresent(entity) {
|
|
21013
|
+
const missing = [];
|
|
20740
21014
|
walkSDKGenFields(entity.fields, (field) => {
|
|
21015
|
+
if (typeof field.id !== "number") {
|
|
21016
|
+
return;
|
|
21017
|
+
}
|
|
20741
21018
|
if (typeof field.key === "string" && field.key.trim() !== "") {
|
|
20742
|
-
|
|
21019
|
+
return;
|
|
20743
21020
|
}
|
|
21021
|
+
missing.push({
|
|
21022
|
+
id: field.id,
|
|
21023
|
+
label: typeof field.label === "string" && field.label.trim() ? field.label.trim() : `field ${field.id}`
|
|
21024
|
+
});
|
|
21025
|
+
});
|
|
21026
|
+
if (missing.length === 0) {
|
|
21027
|
+
return;
|
|
21028
|
+
}
|
|
21029
|
+
const entityName = typeof entity.name === "string" && entity.name.trim() ? entity.name.trim() : `entity ${String(entity.id ?? "")}`.trim();
|
|
21030
|
+
const preview = missing.slice(0, 8).map((field) => `${entityName}.${field.label} (${field.id})`);
|
|
21031
|
+
const suffix = missing.length > preview.length ? `, and ${missing.length - preview.length} more` : "";
|
|
21032
|
+
throw new CLIError("SDK_GEN_FIELD_KEY_REQUIRED", `missing field.key values: ${preview.join(", ")}${suffix}`, 2, {
|
|
21033
|
+
operation: "dev sdk-gen",
|
|
21034
|
+
issues: missing.map((field) => ({
|
|
21035
|
+
path: `entity.${String(entity.id ?? "unknown")}.field.${field.id}.key`,
|
|
21036
|
+
message: `field.key is required for ${entityName}.${field.label}`
|
|
21037
|
+
})),
|
|
21038
|
+
suggestions: [
|
|
21039
|
+
"set stable field.key values in Arcubase admin before running sdk-gen",
|
|
21040
|
+
"rerun arcubase-admin dev sdk-gen after the schema keys are complete"
|
|
21041
|
+
]
|
|
20744
21042
|
});
|
|
20745
|
-
return keys;
|
|
20746
21043
|
}
|
|
20747
|
-
function
|
|
20748
|
-
const
|
|
20749
|
-
|
|
20750
|
-
|
|
20751
|
-
|
|
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();
|
|
20752
21055
|
}
|
|
20753
|
-
|
|
20754
|
-
|
|
20755
|
-
index++;
|
|
21056
|
+
if (typeof ingress.hashId === "string" && ingress.hashId.trim()) {
|
|
21057
|
+
return ingress.hashId.trim();
|
|
20756
21058
|
}
|
|
20757
|
-
|
|
20758
|
-
usedKeys.add(key);
|
|
20759
|
-
return key;
|
|
21059
|
+
return "";
|
|
20760
21060
|
}
|
|
20761
|
-
function
|
|
20762
|
-
|
|
20763
|
-
|
|
20764
|
-
|
|
20765
|
-
|
|
20766
|
-
|
|
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;
|
|
20767
21078
|
}
|
|
20768
|
-
if (
|
|
20769
|
-
|
|
20770
|
-
return;
|
|
21079
|
+
if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(key)) {
|
|
21080
|
+
invalidKey.push(key);
|
|
20771
21081
|
}
|
|
20772
|
-
|
|
20773
|
-
|
|
20774
|
-
|
|
20775
|
-
|
|
20776
|
-
|
|
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
|
+
}
|
|
20777
21122
|
}
|
|
20778
21123
|
async function executeDevSDKGen(runtimeEnv, flags, fetchImpl) {
|
|
20779
21124
|
validateDevSDKGenFlags(flags);
|
|
@@ -20801,8 +21146,7 @@ async function executeDevSDKGen(runtimeEnv, flags, fetchImpl) {
|
|
|
20801
21146
|
if (entityRefs.length === 0) {
|
|
20802
21147
|
throw new CLIError("SDK_GEN_NO_ENTITIES", "sdk-gen could not find entities in app detail response", 2);
|
|
20803
21148
|
}
|
|
20804
|
-
const
|
|
20805
|
-
const initializedFieldKeys = [];
|
|
21149
|
+
const ingresses = [];
|
|
20806
21150
|
for (const entityRef of entityRefs) {
|
|
20807
21151
|
const entityEndpoint = `/api/apps/${encodeURIComponent(appId)}/entity/${encodeURIComponent(entityRef.id)}`;
|
|
20808
21152
|
const entityDetail = await requestJSON(runtimeEnv, "GET", entityEndpoint, void 0, fetchImpl);
|
|
@@ -20810,21 +21154,22 @@ async function executeDevSDKGen(runtimeEnv, flags, fetchImpl) {
|
|
|
20810
21154
|
if (!isRecord4(entityPayload)) {
|
|
20811
21155
|
throw new CLIError("SDK_GEN_INVALID_SCHEMA", "sdk-gen expected entity detail response data", 2);
|
|
20812
21156
|
}
|
|
20813
|
-
|
|
20814
|
-
|
|
20815
|
-
|
|
20816
|
-
|
|
20817
|
-
|
|
20818
|
-
|
|
20819
|
-
|
|
20820
|
-
|
|
20821
|
-
}
|
|
20822
|
-
|
|
20823
|
-
}
|
|
21157
|
+
assertSDKGenFieldKeysPresent(entityPayload);
|
|
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);
|
|
20824
21169
|
const generated = generateArcubaseProjectSDK({
|
|
20825
21170
|
id: resolveSDKGenAppId(appId, appPayload),
|
|
20826
21171
|
name: typeof appPayload.name === "string" ? appPayload.name : void 0,
|
|
20827
|
-
|
|
21172
|
+
ingresses
|
|
20828
21173
|
});
|
|
20829
21174
|
const absoluteOutDir = path2.resolve(process.cwd(), outDir);
|
|
20830
21175
|
for (const file2 of generated.files) {
|
|
@@ -20841,8 +21186,7 @@ async function executeDevSDKGen(runtimeEnv, flags, fetchImpl) {
|
|
|
20841
21186
|
status: appDetail.status,
|
|
20842
21187
|
data: {
|
|
20843
21188
|
out: absoluteOutDir,
|
|
20844
|
-
files: generated.files.map((file2) => file2.path)
|
|
20845
|
-
initializedFieldKeys
|
|
21189
|
+
files: generated.files.map((file2) => file2.path)
|
|
20846
21190
|
}
|
|
20847
21191
|
};
|
|
20848
21192
|
}
|