zenstack 0.3.9 → 0.3.11

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.
@@ -6205,4 +6205,4 @@ See: https://chevrotain.io/docs/changes/BREAKING_CHANGES.html#_7-0-0`)}return r}
6205
6205
  "imports": [],
6206
6206
  "interfaces": [],
6207
6207
  "usedGrammars": []
6208
- }`);var QL={languageId:"zmodel",fileExtensions:[".zmodel"],caseInsensitive:!1},NE={AstReflection:()=>new ms},DE={Grammar:()=>SE(),LanguageMetaData:()=>QL,parser:{}};var an=St(Dn()),IE=St(fn());function bi(r,e){let t=r.reduce((n,i)=>{var o;return n[i.name]=(o=n[i.name])!=null?o:[],n[i.name].push(i),n},{});for(let[n,i]of Object.entries(t))i.length>1&&e("error",`Duplicated declaration name "${n}"`,{node:i[1]})}function Ol(r){if(Dl(r)&&typeof r.value=="string")return r.value}function eF(r,e){switch(r){case"Any":return!0;case"Float":return e==="Any"||e==="Int"||e==="Float";default:return e==="Any"||e===r}}function ny(r){switch(r){case"Any":case"Boolean":case"String":case"DateTime":case"Int":case"Float":case"Null":return r;case"BigInt":return"Int";case"Decimal":return"Float";case"Json":case"Bytes":return"Any"}}function wE(r,e,t){var s,u;let n=r.$resolvedType;if(!n)return!1;let i=e.type.type,o=e.type.array,a=e.type.reference;if(i){if(typeof(n==null?void 0:n.decl)!="string")return!1;if(i==="FieldReference")return o?RE(r.value)&&!r.value.items.find(c=>!ey(c)||!ea(c.target.ref)):ey(r.value)&&ea(r.value.target.ref);if(i==="ContextType")if(ea(t.$container)){if(!((u=(s=t.$container)==null?void 0:s.type)!=null&&u.type))return!1;i=ny(t.$container.type.type)}else i="Any";return eF(i,n.decl)&&o===n.array}else return(a==null?void 0:a.ref)===n.decl&&o===n.array}var $l=class extends an.DefaultLinker{constructor(t){super(t);this.descriptions=t.workspace.AstNodeDescriptionProvider}link(i){return no(this,arguments,function*(t,n=IE.CancellationToken.None){var o,a;if(!(((o=t.parseResult.lexerErrors)==null?void 0:o.length)>0||((a=t.parseResult.parserErrors)==null?void 0:a.length)>0)){for(let s of(0,an.streamContents)(t.parseResult.value))yield(0,an.interruptAndCheck)(n),this.resolve(s,t);t.state=an.DocumentState.Linked}})}linkReference(t,n,i,o){if(!this.resolveFromScopeProviders(t,n,i,o)){let a=t[n];this.doLink({reference:a,container:t,property:n},i)}}resolveFromScopeProviders(t,n,i,o){let a=t[n];for(let s of o){let u=s(a.$refText);if(u)return a._ref=u,a._nodeDescription=this.descriptions.createDescription(u,u.name,i),u}return null}resolve(t,n,i=[]){if(!t.$resolvedType)switch(t.$type){case Nl:this.resolveLiteral(t);break;case Sl:this.resolveInvocation(t,n,i);break;case kl:this.resolveArray(t,n,i);break;case wl:this.resolveReference(t,n,i);break;case Zh:this.resolveMemberAccess(t,n,i);break;case ry:this.resolveUnary(t,n,i);break;case Jh:this.resolveBinary(t,n,i);break;case ty:this.resolveThis(t,n,i);break;case Qh:this.resolveNull(t,n,i);break;case AE:this.resolveAttributeArg(t,n,i);break;default:this.resolveDefault(t,n,i);break}}resolveBinary(t,n,i){switch(t.operator){case">":case">=":case"<":case"<=":case"==":case"!=":case"&&":case"||":this.resolve(t.left,n,i),this.resolve(t.right,n,i),this.resolveToBuiltinTypeOrDecl(t,"Boolean");break;case"?":case"!":case"^":this.resolveCollectionPredicate(t,n,i);break;default:throw Error(`Unsupported binary operator: ${t.operator}`)}}resolveUnary(t,n,i){this.resolve(t.operand,n,i),t.$resolvedType=t.operand.$resolvedType}resolveReference(t,n,i){this.linkReference(t,"target",n,i),t.args.forEach(o=>this.resolve(o,n,i)),t.target.ref&&(t.target.ref.$type===xl?this.resolveToBuiltinTypeOrDecl(t,t.target.ref.$container):this.resolveToDeclaredType(t,t.target.ref.type))}resolveArray(t,n,i){t.items.forEach(a=>this.resolve(a,n,i));let o=t.items[0].$resolvedType;o!=null&&o.decl&&this.resolveToBuiltinTypeOrDecl(t,o.decl,!0)}resolveInvocation(t,n,i){if(this.linkReference(t,"function",n,i),t.args.forEach(o=>this.resolve(o,n,i)),t.function.ref){let o=t.function.ref;this.resolveToDeclaredType(t,o.returnType)}}resolveLiteral(t){let n=typeof t.value=="string"?"String":typeof t.value=="boolean"?"Boolean":typeof t.value=="number"?"Int":void 0;n&&this.resolveToBuiltinTypeOrDecl(t,n)}resolveMemberAccess(t,n,i){this.resolve(t.operand,n,i);let o=t.operand.$resolvedType;if(o&&!o.array&&Vi(o.decl)){let a=o.decl;i=[u=>a.fields.find(c=>c.name===u),...i]}this.linkReference(t,"member",n,i),t.member.ref&&this.resolveToDeclaredType(t,t.member.ref.type)}resolveCollectionPredicate(t,n,i){this.resolve(t.left,n,i);let o=t.left.$resolvedType;if(o&&Vi(o.decl)&&o.array){let a=o.decl;i=[u=>a.fields.find(c=>c.name===u),...i],this.resolve(t.right,n,i),this.resolveToBuiltinTypeOrDecl(t,"Boolean")}else console.warn("Unresolved collection predicate")}resolveThis(t,n,i){let o=t.$container;for(;o&&!Vi(o);)o=o.$container;o&&this.resolveToBuiltinTypeOrDecl(t,o)}resolveNull(t,n,i){this.resolveToBuiltinTypeOrDecl(t,"Null")}resolveAttributeArg(t,n,i){this.resolve(t.value,n,i),t.$resolvedType=t.value.$resolvedType}resolveDefault(t,n,i){for(let[o,a]of Object.entries(t))o.startsWith("$")||(0,an.isReference)(a)&&this.linkReference(t,o,n,i);for(let o of(0,an.streamContents)(t))this.resolve(o,n,i)}resolveToDeclaredType(t,n){if(n.type){let i=ny(n.type);t.$resolvedType={decl:i,array:n.array}}else n.reference&&(t.$resolvedType={decl:n.reference.ref,array:n.array})}resolveToBuiltinTypeOrDecl(t,n,i=!1){t.$resolvedType={decl:n,array:i}}};var ra=St(Dn());var ta=class extends ra.DefaultScopeComputation{constructor(t){super(t);this.services=t}computeExports(t,n){return no(this,null,function*(){let i=yield As(ta.prototype,this,"computeExports").call(this,t,n);for(let o of(0,ra.streamAllContents)(t.parseResult.value))if(n&&(yield(0,ra.interruptAndCheck)(n)),PE(o)){let a=this.services.workspace.AstNodeDescriptionProvider.createDescription(o,o.name,t);i.push(a)}return i})}};var LE=St(Dn());var iy=["postgresql","mysql","sqlite","sqlserver"],OE=["String","Int","Float","Decimal","BigInt","Boolean","Bytes","DateTime"],Ml="stdlib.zmodel";var hs=class{validate(e,t){var n;bi(e.declarations,t),(n=e.$document)!=null&&n.uri.path.endsWith(Ml)||this.validateDataSources(e,t)}validateDataSources(e,t){let n=e.declarations.filter(i=>CE(i));n.length===0?t("error","Model must define a datasource",{node:e}):n.length>1&&t("error","Multiple datasource declarations are not allowed",{node:n[1]})}};var tF=["provider","url","shadowDatabaseUrl","relationMode"],ys=class{validate(e,t){bi(e.fields,t),this.validateFields(e,t),this.validateProvider(e,t),this.validateUrl(e,t),this.validateRelationMode(e,t)}validateFields(e,t){e.fields.forEach(n=>{tF.includes(n.name)||t("error",`Unexpected field "${n.name}"`,{node:n})})}validateProvider(e,t){let n=e.fields.find(o=>o.name==="provider");if(!n){t("error",'datasource must include a "provider" field',{node:e});return}let i=Ol(n.value);i?iy.includes(i)||t("error",`Provider "${i}" is not supported. Choose from ${iy.map(o=>'"'+o+'"').join(" | ")}.`,{node:n.value}):t("error",'"provider" must be set to a string literal',{node:n.value})}validateUrl(e,t){var i;e.fields.find(o=>o.name==="url")||t("error",'datasource must include a "url" field',{node:e});for(let o of["url","shadowDatabaseUrl"]){let a=e.fields.find(u=>u.name===o);if(!a)continue;!Ol(a.value)&&!(kE(a.value)&&((i=a.value.function.ref)==null?void 0:i.name)==="env")&&t("error",`"${o}" must be set to a string literal or an invocation of "env" function`,{node:a.value})}}validateRelationMode(e,t){let n=e.fields.find(i=>i.name==="relationMode");if(n){let i=Ol(n.value);(!i||!["foreignKeys","prisma"].includes(i))&&t("error",'"relationMode" must be set to "foreignKeys" or "prisma"',{node:n.value})}}};var ME=St($E()),gs=class{validate(e,t){bi(e.fields,t),this.validateFields(e,t),this.validateAttributes(e,t)}validateFields(e,t){let n=e.fields.filter(i=>i.attributes.find(o=>{var a;return((a=o.decl.ref)==null?void 0:a.name)==="@id"}));n.length===0?t("error","Model must include a field with @id attribute",{node:e}):n.length>1?t("error","Model can include at most one field with @id attribute",{node:e}):(n[0].type.optional&&t("error","Field with @id attribute must not be optional",{node:n[0]}),(n[0].type.array||!n[0].type.type||!OE.includes(n[0].type.type))&&t("error","Field with @id attribute must be of scalar type",{node:n[0]})),e.fields.forEach(i=>this.validateField(i,t))}validateField(e,t){var n;e.type.array&&e.type.optional&&t("error","Optional lists are not supported. Use either `Type[]` or `Type?`",{node:e.type}),e.attributes.forEach(i=>this.validateAttributeApplication(i,t)),Vi((n=e.type.reference)==null?void 0:n.ref)&&this.validateRelationField(e,t)}validateAttributes(e,t){e.attributes.forEach(n=>{this.validateAttributeApplication(n,t)})}validateAttributeApplication(e,t){let n=e.decl.ref;if(!n)return;let i=e.$container;if(n.name==="@@@targetField"&&!_E(i)){t("error",`attribute "${n.name}" can only be used on attribute declarations`,{node:e});return}ea(i)&&!this.isValidAttributeTarget(n,i)&&t("error",`attribute "${n.name}" cannot be used on this type of field`,{node:e});let o=new Set;for(let s of e.args){let u;if(s.name){if(u=n.params.find(c=>c.name===s.name),!u)return t("error",`Attribute "${n.name}" doesn't have a parameter named "${s.name}"`,{node:s}),!1}else if(u=n.params.find(c=>c.default&&!o.has(c)),!u)return t("error","Unexpected unnamed argument",{node:s}),!1;if(!wE(s,u,e))return t("error","Value is not assignable to parameter",{node:s}),!1;if(o.has(u))return t("error",`Parameter "${u.name}" is already provided`,{node:s}),!1;o.add(u),s.$resolvedParam=u}let a=n.params.filter(s=>!s.type.optional&&!o.has(s));return a.length>0?(t("error",`Required ${(0,ME.default)("parameter",a.length)} not provided: ${a.map(s=>s.name).join(", ")}`,{node:e}),!1):!0}isValidAttributeTarget(e,t){var a;let n=e.attributes.find(s=>{var u;return((u=s.decl.ref)==null?void 0:u.name)==="@@@targetField"});if(!n)return!0;let i=n.args[0].value.items.map(s=>{var u;return(u=s.target.ref)==null?void 0:u.name}),o=!1;for(let s of i){switch(s){case"StringField":o=o||t.type.type==="String";break;case"IntField":o=o||t.type.type==="Int";break;case"FloatField":o=o||t.type.type==="Float";break;case"DecimalField":o=o||t.type.type==="Decimal";break;case"BooleanField":o=o||t.type.type==="Boolean";break;case"DateTimeField":o=o||t.type.type==="DateTime";break;case"JsonField":o=o||t.type.type==="Json";break;case"BytesField":o=o||t.type.type==="Bytes";break;case"ModelField":o=o||Vi((a=t.type.reference)==null?void 0:a.ref);break;default:break}if(o)break}return o}parseRelation(e,t){let n=e.attributes.find(u=>{var c;return((c=u.decl.ref)==null?void 0:c.name)==="@relation"}),i,o,a,s=!0;if(!n)return{attr:n,name:i,fields:o,references:a,valid:!0};for(let u of n.args)!u.name||u.name==="name"?Dl(u.value)&&(i=u.value.value):u.name==="fields"?(o=u.value.items,o.length===0&&(t&&t("error",'"fields" value cannot be emtpy',{node:u}),s=!1)):u.name==="references"&&(a=u.value.items,a.length===0&&(t&&t("error",'"references" value cannot be emtpy',{node:u}),s=!1));return{attr:n,name:i,fields:o,references:a,valid:s}}validateRelationField(e,t){var c,l,d,g;let n=this.parseRelation(e,t);if(!n.valid)return;let i=e.type.reference.ref,o=i.fields.filter(h=>{var p;return((p=h.type.reference)==null?void 0:p.ref)===e.$container});if(o=o.filter(h=>{let p=this.parseRelation(h);return p.valid&&p.name===n.name}),o.length===0){t("error",`The relation field "${e.name}" on model "${e.$container.name}" is missing an opposite relation field on model "${i.name}"`,{node:e});return}else if(o.length>1){o.forEach(h=>t("error",`Fields ${o.map(p=>'"'+p.name+'"').join(", ")} on model "${i.name}" refer to the same relation to model "${e.$container.name}"`,{node:h}));return}let a=o[0],s=this.parseRelation(a),u;if(((c=n==null?void 0:n.references)==null?void 0:c.length)&&((l=n.fields)==null?void 0:l.length))if((s==null?void 0:s.references)||(s==null?void 0:s.fields)){t("error",'"fields" and "references" must be provided only on one side of relation field',{node:a});return}else u=a;else if(((d=s==null?void 0:s.references)==null?void 0:d.length)&&((g=s.fields)==null?void 0:g.length))if((n==null?void 0:n.references)||(n==null?void 0:n.fields)){t("error",'"fields" and "references" must be provided only on one side of relation field',{node:e});return}else u=e;else{[e,a].forEach(h=>t("error",'Field for one side of relation must carry @relation attribute with both "fields" and "references" fields',{node:h}));return}if(!u.type.array&&!u.type.optional){t("error","Relation field needs to be list or optional",{node:u});return}}};var vs=class{validate(e,t){}};var Ts=class{validate(e,t){bi(e.fields,t)}};var Ll=class extends LE.ValidationRegistry{constructor(e){super(e);let t=e.validation.ZModelValidator,n={Model:t.checkModel,DataSource:t.checkDataSource,DataModel:t.checkDataModel,Enum:t.checkEnum,Attribute:t.checkAttribute};this.register(n,t)}},Fl=class{shouldCheck(e){let t,n=e;for(;n;){if(n.$document){t=n.$document;break}n=n.$container}return(t==null?void 0:t.parseResult.lexerErrors.length)===0&&(t==null?void 0:t.parseResult.parserErrors.length)===0}checkModel(e,t){this.shouldCheck(e)&&new hs().validate(e,t)}checkDataSource(e,t){this.shouldCheck(e)&&new ys().validate(e,t)}checkDataModel(e,t){this.shouldCheck(e)&&new gs().validate(e,t)}checkEnum(e,t){this.shouldCheck(e)&&new Ts().validate(e,t)}checkAttribute(e,t){this.shouldCheck(e)&&new vs().validate(e,t)}};var UE=St(Ne()),HE=St(yu());var FE=St(Dn()),qE=St(require("path")),jE=St(gn());var Yi=class extends FE.DefaultWorkspaceManager{loadAdditionalDocuments(e,t){return no(this,null,function*(){yield As(Yi.prototype,this,"loadAdditionalDocuments").call(this,e,t);let n=jE.URI.file(qE.default.join(__dirname,"../res",Ml));console.log(`Adding stdlib document from ${n}`);let i=this.langiumDocuments.getOrCreateDocument(n);t(i)})}};var ql=St(Dn()),GE=St(Ne()),Rs=class{constructor(e){this.nameProvider=e.references.NameProvider,this.references=e.references.References,this.grammarConfig=e.parser.GrammarConfig}getDefinition(e,t){let n=e.parseResult.value;if(n.$cstNode){let i=n.$cstNode,o=(0,ql.findDeclarationNodeAtOffset)(i,e.textDocument.offsetAt(t.position),this.grammarConfig.nameRegexp);if(o)return this.collectLocationLinks(o,t)}}collectLocationLinks(e,t){var i;let n=this.findLink(e);if(n&&!n.targetDocument.textDocument.uri.endsWith("stdlib.zmodel"))return[GE.LocationLink.create(n.targetDocument.textDocument.uri,((i=n.target.element.$cstNode)!=null?i:n.target).range,n.target.range,n.source.range)]}findLink(e){let t=this.references.findDeclarationNode(e);if(t!=null&&t.element){let n=(0,ql.getDocument)(t.element);if(t&&n)return{source:e,target:t,targetDocument:n}}}};var rF={references:{ScopeComputation:r=>new ta(r),Linker:r=>new $l(r)},validation:{ValidationRegistry:r=>new Ll(r),ZModelValidator:()=>new Fl},lsp:{DefinitionProvider:r=>new Rs(r)}};function nF(r){return{ServiceRegistry:()=>new st.DefaultServiceRegistry,lsp:{Connection:()=>r.connection,LanguageServer:e=>new st.DefaultLanguageServer(e)},workspace:{LangiumDocuments:e=>new st.DefaultLangiumDocuments(e),LangiumDocumentFactory:e=>new st.DefaultLangiumDocumentFactory(e),DocumentBuilder:e=>new st.DefaultDocumentBuilder(e),TextDocuments:()=>new UE.TextDocuments(HE.TextDocument),TextDocumentFactory:e=>new st.DefaultTextDocumentFactory(e),IndexManager:e=>new st.DefaultIndexManager(e),WorkspaceManager:e=>new Yi(e),FileSystemProvider:e=>r.fileSystemProvider(e),MutexLock:()=>new st.MutexLock,ConfigurationProvider:e=>new st.DefaultConfigurationProvider(e)}}}function WE(r){let e=(0,st.inject)(nF(r),NE),t=(0,st.inject)((0,st.createDefaultModule)({shared:e}),DE,rF);return e.ServiceRegistry.register(t),{shared:e,ZModel:t}}var iF=(0,jl.createConnection)(jl.ProposedFeatures.all),{shared:oF}=WE(fy({connection:iF},BE.NodeFileSystem));(0,KE.startLanguageServer)(oF);
6208
+ }`);var QL={languageId:"zmodel",fileExtensions:[".zmodel"],caseInsensitive:!1},NE={AstReflection:()=>new ms},DE={Grammar:()=>SE(),LanguageMetaData:()=>QL,parser:{}};var an=St(Dn()),IE=St(fn());function bi(r,e){let t=r.reduce((n,i)=>{var o;return n[i.name]=(o=n[i.name])!=null?o:[],n[i.name].push(i),n},{});for(let[n,i]of Object.entries(t))i.length>1&&e("error",`Duplicated declaration name "${n}"`,{node:i[1]})}function Ol(r){if(Dl(r)&&typeof r.value=="string")return r.value}function eF(r,e){switch(r){case"Any":return!0;case"Float":return e==="Any"||e==="Int"||e==="Float";default:return e==="Any"||e===r}}function ny(r){switch(r){case"Any":case"Boolean":case"String":case"DateTime":case"Int":case"Float":case"Null":return r;case"BigInt":return"Int";case"Decimal":return"Float";case"Json":case"Bytes":return"Any"}}function wE(r,e,t){var s,u;let n=r.$resolvedType;if(!n)return!1;let i=e.type.type,o=e.type.array,a=e.type.reference;if(i){if(typeof(n==null?void 0:n.decl)!="string")return!1;if(i==="FieldReference")return o?RE(r.value)&&!r.value.items.find(c=>!ey(c)||!ea(c.target.ref)):ey(r.value)&&ea(r.value.target.ref);if(i==="ContextType")if(ea(t.$container)){if(!((u=(s=t.$container)==null?void 0:s.type)!=null&&u.type))return!1;i=ny(t.$container.type.type)}else i="Any";return eF(i,n.decl)&&o===n.array}else return(a==null?void 0:a.ref)===n.decl&&o===n.array}var $l=class extends an.DefaultLinker{constructor(t){super(t);this.descriptions=t.workspace.AstNodeDescriptionProvider}link(i){return no(this,arguments,function*(t,n=IE.CancellationToken.None){var o,a;if(!(((o=t.parseResult.lexerErrors)==null?void 0:o.length)>0||((a=t.parseResult.parserErrors)==null?void 0:a.length)>0)){for(let s of(0,an.streamContents)(t.parseResult.value))yield(0,an.interruptAndCheck)(n),this.resolve(s,t);t.state=an.DocumentState.Linked}})}linkReference(t,n,i,o){if(!this.resolveFromScopeProviders(t,n,i,o)){let a=t[n];this.doLink({reference:a,container:t,property:n},i)}}resolveFromScopeProviders(t,n,i,o){let a=t[n];for(let s of o){let u=s(a.$refText);if(u)return a._ref=u,a._nodeDescription=this.descriptions.createDescription(u,u.name,i),u}return null}resolve(t,n,i=[]){if(!t.$resolvedType)switch(t.$type){case Nl:this.resolveLiteral(t);break;case Sl:this.resolveInvocation(t,n,i);break;case kl:this.resolveArray(t,n,i);break;case wl:this.resolveReference(t,n,i);break;case Zh:this.resolveMemberAccess(t,n,i);break;case ry:this.resolveUnary(t,n,i);break;case Jh:this.resolveBinary(t,n,i);break;case ty:this.resolveThis(t,n,i);break;case Qh:this.resolveNull(t,n,i);break;case AE:this.resolveAttributeArg(t,n,i);break;default:this.resolveDefault(t,n,i);break}}resolveBinary(t,n,i){switch(t.operator){case">":case">=":case"<":case"<=":case"==":case"!=":case"&&":case"||":this.resolve(t.left,n,i),this.resolve(t.right,n,i),this.resolveToBuiltinTypeOrDecl(t,"Boolean");break;case"?":case"!":case"^":this.resolveCollectionPredicate(t,n,i);break;default:throw Error(`Unsupported binary operator: ${t.operator}`)}}resolveUnary(t,n,i){this.resolve(t.operand,n,i),t.$resolvedType=t.operand.$resolvedType}resolveReference(t,n,i){this.linkReference(t,"target",n,i),t.args.forEach(o=>this.resolve(o,n,i)),t.target.ref&&(t.target.ref.$type===xl?this.resolveToBuiltinTypeOrDecl(t,t.target.ref.$container):this.resolveToDeclaredType(t,t.target.ref.type))}resolveArray(t,n,i){t.items.forEach(a=>this.resolve(a,n,i));let o=t.items[0].$resolvedType;o!=null&&o.decl&&this.resolveToBuiltinTypeOrDecl(t,o.decl,!0)}resolveInvocation(t,n,i){if(this.linkReference(t,"function",n,i),t.args.forEach(o=>this.resolve(o,n,i)),t.function.ref){let o=t.function.ref;this.resolveToDeclaredType(t,o.returnType)}}resolveLiteral(t){let n=typeof t.value=="string"?"String":typeof t.value=="boolean"?"Boolean":typeof t.value=="number"?"Int":void 0;n&&this.resolveToBuiltinTypeOrDecl(t,n)}resolveMemberAccess(t,n,i){this.resolve(t.operand,n,i);let o=t.operand.$resolvedType;if(o&&!o.array&&Vi(o.decl)){let a=o.decl;i=[u=>a.fields.find(c=>c.name===u),...i]}this.linkReference(t,"member",n,i),t.member.ref&&this.resolveToDeclaredType(t,t.member.ref.type)}resolveCollectionPredicate(t,n,i){this.resolve(t.left,n,i);let o=t.left.$resolvedType;if(o&&Vi(o.decl)&&o.array){let a=o.decl;i=[u=>a.fields.find(c=>c.name===u),...i],this.resolve(t.right,n,i),this.resolveToBuiltinTypeOrDecl(t,"Boolean")}else console.warn("Unresolved collection predicate")}resolveThis(t,n,i){let o=t.$container;for(;o&&!Vi(o);)o=o.$container;o&&this.resolveToBuiltinTypeOrDecl(t,o)}resolveNull(t,n,i){this.resolveToBuiltinTypeOrDecl(t,"Null")}resolveAttributeArg(t,n,i){this.resolve(t.value,n,i),t.$resolvedType=t.value.$resolvedType}resolveDefault(t,n,i){for(let[o,a]of Object.entries(t))o.startsWith("$")||(0,an.isReference)(a)&&this.linkReference(t,o,n,i);for(let o of(0,an.streamContents)(t))this.resolve(o,n,i)}resolveToDeclaredType(t,n){if(n.type){let i=ny(n.type);t.$resolvedType={decl:i,array:n.array}}else n.reference&&(t.$resolvedType={decl:n.reference.ref,array:n.array})}resolveToBuiltinTypeOrDecl(t,n,i=!1){t.$resolvedType={decl:n,array:i}}};var ra=St(Dn());var ta=class extends ra.DefaultScopeComputation{constructor(t){super(t);this.services=t}computeExports(t,n){return no(this,null,function*(){let i=yield As(ta.prototype,this,"computeExports").call(this,t,n);for(let o of(0,ra.streamAllContents)(t.parseResult.value))if(n&&(yield(0,ra.interruptAndCheck)(n)),PE(o)){let a=this.services.workspace.AstNodeDescriptionProvider.createDescription(o,o.name,t);i.push(a)}return i})}};var LE=St(Dn());var iy=["sqlite","postgresql","mysql","sqlserver","cockroachdb"],OE=["String","Int","Float","Decimal","BigInt","Boolean","Bytes","DateTime"],Ml="stdlib.zmodel";var hs=class{validate(e,t){var n;bi(e.declarations,t),(n=e.$document)!=null&&n.uri.path.endsWith(Ml)||this.validateDataSources(e,t)}validateDataSources(e,t){let n=e.declarations.filter(i=>CE(i));n.length===0?t("error","Model must define a datasource",{node:e}):n.length>1&&t("error","Multiple datasource declarations are not allowed",{node:n[1]})}};var tF=["provider","url","shadowDatabaseUrl","relationMode"],ys=class{validate(e,t){bi(e.fields,t),this.validateFields(e,t),this.validateProvider(e,t),this.validateUrl(e,t),this.validateRelationMode(e,t)}validateFields(e,t){e.fields.forEach(n=>{tF.includes(n.name)||t("error",`Unexpected field "${n.name}"`,{node:n})})}validateProvider(e,t){let n=e.fields.find(o=>o.name==="provider");if(!n){t("error",'datasource must include a "provider" field',{node:e});return}let i=Ol(n.value);i?iy.includes(i)||t("error",`Provider "${i}" is not supported. Choose from ${iy.map(o=>'"'+o+'"').join(" | ")}.`,{node:n.value}):t("error",'"provider" must be set to a string literal',{node:n.value})}validateUrl(e,t){var i;e.fields.find(o=>o.name==="url")||t("error",'datasource must include a "url" field',{node:e});for(let o of["url","shadowDatabaseUrl"]){let a=e.fields.find(u=>u.name===o);if(!a)continue;!Ol(a.value)&&!(kE(a.value)&&((i=a.value.function.ref)==null?void 0:i.name)==="env")&&t("error",`"${o}" must be set to a string literal or an invocation of "env" function`,{node:a.value})}}validateRelationMode(e,t){let n=e.fields.find(i=>i.name==="relationMode");if(n){let i=Ol(n.value);(!i||!["foreignKeys","prisma"].includes(i))&&t("error",'"relationMode" must be set to "foreignKeys" or "prisma"',{node:n.value})}}};var ME=St($E()),gs=class{validate(e,t){bi(e.fields,t),this.validateFields(e,t),this.validateAttributes(e,t)}validateFields(e,t){let n=e.fields.filter(i=>i.attributes.find(o=>{var a;return((a=o.decl.ref)==null?void 0:a.name)==="@id"}));n.length===0?t("error","Model must include a field with @id attribute",{node:e}):n.length>1?t("error","Model can include at most one field with @id attribute",{node:e}):(n[0].type.optional&&t("error","Field with @id attribute must not be optional",{node:n[0]}),(n[0].type.array||!n[0].type.type||!OE.includes(n[0].type.type))&&t("error","Field with @id attribute must be of scalar type",{node:n[0]})),e.fields.forEach(i=>this.validateField(i,t))}validateField(e,t){var n;e.type.array&&e.type.optional&&t("error","Optional lists are not supported. Use either `Type[]` or `Type?`",{node:e.type}),e.attributes.forEach(i=>this.validateAttributeApplication(i,t)),Vi((n=e.type.reference)==null?void 0:n.ref)&&this.validateRelationField(e,t)}validateAttributes(e,t){e.attributes.forEach(n=>{this.validateAttributeApplication(n,t)})}validateAttributeApplication(e,t){let n=e.decl.ref;if(!n)return;let i=e.$container;if(n.name==="@@@targetField"&&!_E(i)){t("error",`attribute "${n.name}" can only be used on attribute declarations`,{node:e});return}ea(i)&&!this.isValidAttributeTarget(n,i)&&t("error",`attribute "${n.name}" cannot be used on this type of field`,{node:e});let o=new Set;for(let s of e.args){let u;if(s.name){if(u=n.params.find(c=>c.name===s.name),!u)return t("error",`Attribute "${n.name}" doesn't have a parameter named "${s.name}"`,{node:s}),!1}else if(u=n.params.find(c=>c.default&&!o.has(c)),!u)return t("error","Unexpected unnamed argument",{node:s}),!1;if(!wE(s,u,e))return t("error","Value is not assignable to parameter",{node:s}),!1;if(o.has(u))return t("error",`Parameter "${u.name}" is already provided`,{node:s}),!1;o.add(u),s.$resolvedParam=u}let a=n.params.filter(s=>!s.type.optional&&!o.has(s));return a.length>0?(t("error",`Required ${(0,ME.default)("parameter",a.length)} not provided: ${a.map(s=>s.name).join(", ")}`,{node:e}),!1):!0}isValidAttributeTarget(e,t){var a;let n=e.attributes.find(s=>{var u;return((u=s.decl.ref)==null?void 0:u.name)==="@@@targetField"});if(!n)return!0;let i=n.args[0].value.items.map(s=>{var u;return(u=s.target.ref)==null?void 0:u.name}),o=!1;for(let s of i){switch(s){case"StringField":o=o||t.type.type==="String";break;case"IntField":o=o||t.type.type==="Int";break;case"FloatField":o=o||t.type.type==="Float";break;case"DecimalField":o=o||t.type.type==="Decimal";break;case"BooleanField":o=o||t.type.type==="Boolean";break;case"DateTimeField":o=o||t.type.type==="DateTime";break;case"JsonField":o=o||t.type.type==="Json";break;case"BytesField":o=o||t.type.type==="Bytes";break;case"ModelField":o=o||Vi((a=t.type.reference)==null?void 0:a.ref);break;default:break}if(o)break}return o}parseRelation(e,t){let n=e.attributes.find(u=>{var c;return((c=u.decl.ref)==null?void 0:c.name)==="@relation"}),i,o,a,s=!0;if(!n)return{attr:n,name:i,fields:o,references:a,valid:!0};for(let u of n.args)!u.name||u.name==="name"?Dl(u.value)&&(i=u.value.value):u.name==="fields"?(o=u.value.items,o.length===0&&(t&&t("error",'"fields" value cannot be emtpy',{node:u}),s=!1)):u.name==="references"&&(a=u.value.items,a.length===0&&(t&&t("error",'"references" value cannot be emtpy',{node:u}),s=!1));return{attr:n,name:i,fields:o,references:a,valid:s}}validateRelationField(e,t){var c,l,d,g;let n=this.parseRelation(e,t);if(!n.valid)return;let i=e.type.reference.ref,o=i.fields.filter(h=>{var p;return((p=h.type.reference)==null?void 0:p.ref)===e.$container});if(o=o.filter(h=>{let p=this.parseRelation(h);return p.valid&&p.name===n.name}),o.length===0){t("error",`The relation field "${e.name}" on model "${e.$container.name}" is missing an opposite relation field on model "${i.name}"`,{node:e});return}else if(o.length>1){o.forEach(h=>t("error",`Fields ${o.map(p=>'"'+p.name+'"').join(", ")} on model "${i.name}" refer to the same relation to model "${e.$container.name}"`,{node:h}));return}let a=o[0],s=this.parseRelation(a),u;if(((c=n==null?void 0:n.references)==null?void 0:c.length)&&((l=n.fields)==null?void 0:l.length))if((s==null?void 0:s.references)||(s==null?void 0:s.fields)){t("error",'"fields" and "references" must be provided only on one side of relation field',{node:a});return}else u=a;else if(((d=s==null?void 0:s.references)==null?void 0:d.length)&&((g=s.fields)==null?void 0:g.length))if((n==null?void 0:n.references)||(n==null?void 0:n.fields)){t("error",'"fields" and "references" must be provided only on one side of relation field',{node:e});return}else u=e;else{[e,a].forEach(h=>t("error",'Field for one side of relation must carry @relation attribute with both "fields" and "references" fields',{node:h}));return}if(!u.type.array&&!u.type.optional){t("error","Relation field needs to be list or optional",{node:u});return}}};var vs=class{validate(e,t){}};var Ts=class{validate(e,t){bi(e.fields,t)}};var Ll=class extends LE.ValidationRegistry{constructor(e){super(e);let t=e.validation.ZModelValidator,n={Model:t.checkModel,DataSource:t.checkDataSource,DataModel:t.checkDataModel,Enum:t.checkEnum,Attribute:t.checkAttribute};this.register(n,t)}},Fl=class{shouldCheck(e){let t,n=e;for(;n;){if(n.$document){t=n.$document;break}n=n.$container}return(t==null?void 0:t.parseResult.lexerErrors.length)===0&&(t==null?void 0:t.parseResult.parserErrors.length)===0}checkModel(e,t){this.shouldCheck(e)&&new hs().validate(e,t)}checkDataSource(e,t){this.shouldCheck(e)&&new ys().validate(e,t)}checkDataModel(e,t){this.shouldCheck(e)&&new gs().validate(e,t)}checkEnum(e,t){this.shouldCheck(e)&&new Ts().validate(e,t)}checkAttribute(e,t){this.shouldCheck(e)&&new vs().validate(e,t)}};var UE=St(Ne()),HE=St(yu());var FE=St(Dn()),qE=St(require("path")),jE=St(gn());var Yi=class extends FE.DefaultWorkspaceManager{loadAdditionalDocuments(e,t){return no(this,null,function*(){yield As(Yi.prototype,this,"loadAdditionalDocuments").call(this,e,t);let n=jE.URI.file(qE.default.join(__dirname,"../res",Ml));console.log(`Adding stdlib document from ${n}`);let i=this.langiumDocuments.getOrCreateDocument(n);t(i)})}};var ql=St(Dn()),GE=St(Ne()),Rs=class{constructor(e){this.nameProvider=e.references.NameProvider,this.references=e.references.References,this.grammarConfig=e.parser.GrammarConfig}getDefinition(e,t){let n=e.parseResult.value;if(n.$cstNode){let i=n.$cstNode,o=(0,ql.findDeclarationNodeAtOffset)(i,e.textDocument.offsetAt(t.position),this.grammarConfig.nameRegexp);if(o)return this.collectLocationLinks(o,t)}}collectLocationLinks(e,t){var i;let n=this.findLink(e);if(n&&!n.targetDocument.textDocument.uri.endsWith("stdlib.zmodel"))return[GE.LocationLink.create(n.targetDocument.textDocument.uri,((i=n.target.element.$cstNode)!=null?i:n.target).range,n.target.range,n.source.range)]}findLink(e){let t=this.references.findDeclarationNode(e);if(t!=null&&t.element){let n=(0,ql.getDocument)(t.element);if(t&&n)return{source:e,target:t,targetDocument:n}}}};var rF={references:{ScopeComputation:r=>new ta(r),Linker:r=>new $l(r)},validation:{ValidationRegistry:r=>new Ll(r),ZModelValidator:()=>new Fl},lsp:{DefinitionProvider:r=>new Rs(r)}};function nF(r){return{ServiceRegistry:()=>new st.DefaultServiceRegistry,lsp:{Connection:()=>r.connection,LanguageServer:e=>new st.DefaultLanguageServer(e)},workspace:{LangiumDocuments:e=>new st.DefaultLangiumDocuments(e),LangiumDocumentFactory:e=>new st.DefaultLangiumDocumentFactory(e),DocumentBuilder:e=>new st.DefaultDocumentBuilder(e),TextDocuments:()=>new UE.TextDocuments(HE.TextDocument),TextDocumentFactory:e=>new st.DefaultTextDocumentFactory(e),IndexManager:e=>new st.DefaultIndexManager(e),WorkspaceManager:e=>new Yi(e),FileSystemProvider:e=>r.fileSystemProvider(e),MutexLock:()=>new st.MutexLock,ConfigurationProvider:e=>new st.DefaultConfigurationProvider(e)}}}function WE(r){let e=(0,st.inject)(nF(r),NE),t=(0,st.inject)((0,st.createDefaultModule)({shared:e}),DE,rF);return e.ServiceRegistry.register(t),{shared:e,ZModel:t}}var iF=(0,jl.createConnection)(jl.ProposedFeatures.all),{shared:oF}=WE(fy({connection:iF},BE.NodeFileSystem));(0,KE.startLanguageServer)(oF);
@@ -7,6 +7,30 @@ enum ReferentialAction {
7
7
  * Used with "onUpdate": updates the relation scalar fields if the referenced scalar fields of the dependent record are updated.
8
8
  */
9
9
  Cascade
10
+
11
+ /*
12
+ * Used with "onDelete": prevents the deletion if any referencing records exist.
13
+ * Used with "onUpdate": prevents the identifier of a referenced record from being changed.
14
+ */
15
+ Restrict
16
+
17
+ /*
18
+ * Similar to 'Restrict', the difference between the two is dependent on the database being used.
19
+ * See details: https://www.prisma.io/docs/concepts/components/prisma-schema/relations/referential-actions#noaction
20
+ */
21
+ NoAction
22
+
23
+ /*
24
+ * Used with "onDelete": the scalar field of the referencing object will be set to NULL.
25
+ * Used with "onUpdate": when updating the identifier of a referenced object, the scalar fields of the referencing objects will be set to NULL.
26
+ */
27
+ SetNull
28
+
29
+ /*
30
+ * Used with "onDelete": the scalar field of the referencing object will be set to the fields default value.
31
+ * Used with "onUpdate": the scalar field of the referencing object will be set to the fields default value.
32
+ */
33
+ SetDefault
10
34
  }
11
35
 
12
36
  /*
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publisher": "zenstack",
4
4
  "displayName": "ZenStack Language Tools",
5
5
  "description": "A toolkit for modeling data and access policies in full-stack development with Next.js and Typescript",
6
- "version": "0.3.9",
6
+ "version": "0.3.11",
7
7
  "author": {
8
8
  "name": "ZenStack Team"
9
9
  },
@@ -65,7 +65,7 @@
65
65
  },
66
66
  "main": "./bundle/extension.js",
67
67
  "dependencies": {
68
- "@zenstackhq/internal": "0.3.9",
68
+ "@zenstackhq/runtime": "0.3.11",
69
69
  "async-exit-hook": "^2.0.1",
70
70
  "change-case": "^4.1.2",
71
71
  "chevrotain": "^9.1.0",
@@ -6,12 +6,108 @@ import fs from 'fs';
6
6
  import { LangiumServices } from 'langium';
7
7
  import { NodeFileSystem } from 'langium/node';
8
8
  import path from 'path';
9
- import { ZenStackGenerator } from '../generator';
9
+ import { installPackage } from 'src/utils/pkg-utils';
10
10
  import { URI } from 'vscode-uri';
11
+ import { ZenStackGenerator } from '../generator';
11
12
  import { GENERATED_CODE_PATH } from '../generator/constants';
12
13
  import { Context, GeneratorError } from '../generator/types';
13
14
  import { CliError } from './cli-error';
14
15
 
16
+ /**
17
+ * Initializes an existing project for ZenStack
18
+ */
19
+ export async function initProject(projectPath: string) {
20
+ const schema = path.join(projectPath, 'zenstack', 'schema.zmodel');
21
+ let schemaGenerated = false;
22
+
23
+ if (fs.existsSync(schema)) {
24
+ console.warn(colors.yellow(`Model already exists: ${schema}`));
25
+ } else {
26
+ // create a default model
27
+ if (!fs.existsSync(path.join(projectPath, 'zenstack'))) {
28
+ fs.mkdirSync(path.join(projectPath, 'zenstack'));
29
+ }
30
+
31
+ fs.writeFileSync(
32
+ schema,
33
+ `// This is a sample model to get you started.
34
+ // Learn how to model you app: https://zenstack.dev/#/modeling-your-app.
35
+
36
+ /*
37
+ * A sample data source using local sqlite db.
38
+ * See how to use a different db: https://zenstack.dev/#/zmodel-data-source.
39
+ */
40
+ datasource db {
41
+ provider = 'sqlite'
42
+ url = 'file:./todo.db'
43
+ }
44
+
45
+ /*
46
+ * User model
47
+ */
48
+ model User {
49
+ id String @id @default(cuid())
50
+ email String @unique @email
51
+ password String @password @omit @length(8, 16)
52
+ posts Post[]
53
+
54
+ // everybody can signup
55
+ @@allow('create', true)
56
+
57
+ // full access by self
58
+ @@allow('all', auth() == this)
59
+ }
60
+
61
+ /*
62
+ * Post model
63
+ */
64
+ model Post {
65
+ id String @id @default(cuid())
66
+ createdAt DateTime @default(now())
67
+ updatedAt DateTime @updatedAt
68
+ title String @length(1, 256)
69
+ content String
70
+ published Boolean @default(false)
71
+ author User? @relation(fields: [authorId], references: [id])
72
+ authorId String?
73
+
74
+ // allow read for all signin users
75
+ @@allow('read', auth() != null && published)
76
+
77
+ // full access by author
78
+ @@allow('all', author == auth())
79
+ }
80
+ `
81
+ );
82
+
83
+ // add zenstack/schema.prisma to .gitignore
84
+ const gitIgnorePath = path.join(projectPath, '.gitignore');
85
+ let gitIgnoreContent = '';
86
+ if (fs.existsSync(gitIgnorePath)) {
87
+ gitIgnoreContent =
88
+ fs.readFileSync(gitIgnorePath, { encoding: 'utf-8' }) + '\n';
89
+ }
90
+
91
+ if (!gitIgnoreContent.includes('zenstack/schema.prisma')) {
92
+ gitIgnoreContent += 'zenstack/schema.prisma\n';
93
+ fs.writeFileSync(gitIgnorePath, gitIgnoreContent);
94
+ }
95
+
96
+ schemaGenerated = true;
97
+ }
98
+
99
+ installPackage('zenstack', true, undefined, projectPath);
100
+ installPackage('@zenstackhq/runtime', false, undefined, projectPath);
101
+
102
+ if (schemaGenerated) {
103
+ console.log(`Sample model generated at: ${colors.green(schema)}
104
+
105
+ Please check the following guide on how to model your app:
106
+ https://zenstack.dev/#/modeling-your-app.
107
+ `);
108
+ }
109
+ }
110
+
15
111
  /**
16
112
  * Loads a zmodel document from a file.
17
113
  * @param fileName File name
@@ -77,7 +173,7 @@ export async function loadDocument(
77
173
  }
78
174
 
79
175
  export async function runGenerator(
80
- options: { schema: string },
176
+ options: { schema: string; packageManager: string },
81
177
  includedGenerators?: string[],
82
178
  clearOutput = true
83
179
  ) {
package/src/cli/index.ts CHANGED
@@ -1,16 +1,27 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { Command, Option } from 'commander';
3
- import { ZModelLanguageMetaData } from '../language-server/generated/module';
4
- import colors from 'colors';
5
- import { execSync } from '../utils/exec-utils';
6
2
  import { paramCase } from 'change-case';
3
+ import colors from 'colors';
4
+ import { Command, Option } from 'commander';
7
5
  import path from 'path';
8
- import { runGenerator } from './cli-util';
6
+ import { ZModelLanguageMetaData } from '../language-server/generated/module';
9
7
  import telemetry from '../telemetry';
8
+ import { execSync } from '../utils/exec-utils';
10
9
  import { CliError } from './cli-error';
10
+ import { initProject, runGenerator } from './cli-util';
11
+
12
+ export const initAction = async (projectPath: string): Promise<void> => {
13
+ await telemetry.trackSpan(
14
+ 'cli:command:start',
15
+ 'cli:command:complete',
16
+ 'cli:command:error',
17
+ { command: 'init' },
18
+ () => initProject(projectPath)
19
+ );
20
+ };
11
21
 
12
22
  export const generateAction = async (options: {
13
23
  schema: string;
24
+ packageManager: string;
14
25
  }): Promise<void> => {
15
26
  await telemetry.trackSpan(
16
27
  'cli:command:start',
@@ -74,7 +85,6 @@ function prismaAction(prismaCmd: string): (...args: any[]) => Promise<void> {
74
85
  }
75
86
 
76
87
  export default async function (): Promise<void> {
77
- // try {
78
88
  await telemetry.trackSpan(
79
89
  'cli:start',
80
90
  'cli:complete',
@@ -97,7 +107,7 @@ export default async function (): Promise<void> {
97
107
  .description(
98
108
  `${colors.bold.blue(
99
109
  'ζ'
100
- )} ZenStack simplifies fullstack development by generating backend services and Typescript clients from a data model.\n\nDocumentation: https://go.zenstack.dev/doc.`
110
+ )} ZenStack is a toolkit for building secure CRUD apps with Next.js + Typescript.\n\nDocumentation: https://go.zenstack.dev/doc.`
101
111
  )
102
112
  .showHelpAfterError()
103
113
  .showSuggestionAfterError();
@@ -107,28 +117,43 @@ export default async function (): Promise<void> {
107
117
  `schema file (with extension ${schemaExtensions})`
108
118
  ).default('./zenstack/schema.zmodel');
109
119
 
120
+ const pmOption = new Option(
121
+ '--package-manager, -p',
122
+ 'package manager to use: "npm", "yarn" or "pnpm"'
123
+ ).default('auto detect');
124
+
110
125
  //#region wraps Prisma commands
111
126
 
127
+ program
128
+ .command('init')
129
+ .description('Set up a new ZenStack project.')
130
+ .addOption(pmOption)
131
+ .argument('<path>', 'project path')
132
+ .action(initAction);
133
+
112
134
  program
113
135
  .command('generate')
114
136
  .description(
115
- 'generates RESTful API and Typescript client for your data model'
137
+ 'Generates RESTful API and Typescript client for your data model.'
116
138
  )
117
139
  .addOption(schemaOption)
140
+ .addOption(pmOption)
118
141
  .action(generateAction);
119
142
 
120
143
  const migrate = program
121
144
  .command('migrate')
122
145
  .description(
123
- `wraps Prisma's ${colors.cyan('migrate')} command`
146
+ `Updates the database schema with migrations\nAlias for ${colors.cyan(
147
+ 'prisma migrate'
148
+ )}.`
124
149
  );
125
150
 
126
151
  migrate
127
152
  .command('dev')
128
153
  .description(
129
- `alias for ${colors.cyan(
154
+ `Creates a migration, apply it to the database, generate db client\nAlias for ${colors.cyan(
130
155
  'prisma migrate dev'
131
- )}\nCreate a migration, apply it to the database, generate db client.`
156
+ )}.`
132
157
  )
133
158
  .addOption(schemaOption)
134
159
  .option(
@@ -142,9 +167,9 @@ export default async function (): Promise<void> {
142
167
  migrate
143
168
  .command('reset')
144
169
  .description(
145
- `alias for ${colors.cyan(
170
+ `Resets your database and apply all migrations\nAlias for ${colors.cyan(
146
171
  'prisma migrate reset'
147
- )}\nReset your database and apply all migrations.`
172
+ )}.`
148
173
  )
149
174
  .addOption(schemaOption)
150
175
  .option('--force', 'Skip the confirmation prompt')
@@ -153,9 +178,9 @@ export default async function (): Promise<void> {
153
178
  migrate
154
179
  .command('deploy')
155
180
  .description(
156
- `alias for ${colors.cyan(
181
+ `Applies pending migrations to the database in production/staging\nAlias for ${colors.cyan(
157
182
  'prisma migrate deploy'
158
- )}\nApply pending migrations to the database in production/staging.`
183
+ )}.`
159
184
  )
160
185
  .addOption(schemaOption)
161
186
  .action(prismaAction('migrate'));
@@ -163,22 +188,26 @@ export default async function (): Promise<void> {
163
188
  migrate
164
189
  .command('status')
165
190
  .description(
166
- `alias for ${colors.cyan(
191
+ `Checks the status of migrations in the production/staging database\nAlias for ${colors.cyan(
167
192
  'prisma migrate status'
168
- )}\nCheck the status of migrations in the production/staging database.`
193
+ )}.`
169
194
  )
170
195
  .addOption(schemaOption)
171
196
  .action(prismaAction('migrate'));
172
197
 
173
198
  const db = program
174
199
  .command('db')
175
- .description(`wraps Prisma's ${colors.cyan('db')} command`);
200
+ .description(
201
+ `Manages your database schema and lifecycle during development\nAlias for ${colors.cyan(
202
+ 'prisma db'
203
+ )}.`
204
+ );
176
205
 
177
206
  db.command('push')
178
207
  .description(
179
- `alias for ${colors.cyan(
208
+ `Pushes the Prisma schema state to the database\nAlias for ${colors.cyan(
180
209
  'prisma db push'
181
- )}\nPush the Prisma schema state to the database.`
210
+ )}.`
182
211
  )
183
212
  .addOption(schemaOption)
184
213
  .option('--accept-data-loss', 'Ignore data loss warnings')
@@ -187,9 +216,9 @@ export default async function (): Promise<void> {
187
216
  program
188
217
  .command('studio')
189
218
  .description(
190
- `wraps Prisma's ${colors.cyan(
191
- 'studio'
192
- )} command. Browse your data with Prisma Studio.`
219
+ `Browses your data with Prisma Studio\nAlias for ${colors.cyan(
220
+ 'prisma studio'
221
+ )}.`
193
222
  )
194
223
  .addOption(schemaOption)
195
224
  .option('-p --port <port>', 'Port to start Studio in')
@@ -1,4 +1,4 @@
1
- export const INTERNAL_PACKAGE = '@zenstackhq/internal';
1
+ export const RUNTIME_PACKAGE = '@zenstackhq/runtime';
2
2
  export const GUARD_FIELD_NAME = 'zenstack_guard';
3
3
  export const TRANSACTION_FIELD_NAME = 'zenstack_transaction';
4
4
  export const API_ROUTE_NAME = 'zenstack';
@@ -10,12 +10,12 @@ import {
10
10
  PolicyKind,
11
11
  PolicyOperationKind,
12
12
  RuntimeAttribute,
13
- } from '@zenstackhq/internal';
13
+ } from '@zenstackhq/runtime/server';
14
14
  import path from 'path';
15
15
  import { Project, SourceFile, VariableDeclarationKind } from 'ts-morph';
16
16
  import {
17
17
  GUARD_FIELD_NAME,
18
- INTERNAL_PACKAGE,
18
+ RUNTIME_PACKAGE,
19
19
  UNKNOWN_USER_ID,
20
20
  } from '../constants';
21
21
  import { Context } from '../types';
@@ -38,7 +38,7 @@ export default class QueryGuardGenerator {
38
38
 
39
39
  sf.addImportDeclaration({
40
40
  namedImports: [{ name: 'QueryContext' }],
41
- moduleSpecifier: INTERNAL_PACKAGE,
41
+ moduleSpecifier: `${RUNTIME_PACKAGE}/server`,
42
42
  isTypeOnly: true,
43
43
  });
44
44
 
@@ -5,7 +5,7 @@ import { paramCase } from 'change-case';
5
5
  import { DataModel } from '@lang/generated/ast';
6
6
  import colors from 'colors';
7
7
  import { extractDataModelsWithAllowRules } from '../ast-utils';
8
- import { API_ROUTE_NAME } from '../constants';
8
+ import { API_ROUTE_NAME, RUNTIME_PACKAGE } from '../constants';
9
9
 
10
10
  /**
11
11
  * Generate react data query hooks code
@@ -51,7 +51,9 @@ export default class ReactHooksGenerator implements Generator {
51
51
  moduleSpecifier: '../../.prisma',
52
52
  });
53
53
  sf.addStatements([
54
- `import { request, validate, ServerErrorCode, RequestOptions } from '@zenstackhq/runtime/client';`,
54
+ `import * as request from '${RUNTIME_PACKAGE}/lib/request';`,
55
+ `import { ServerErrorCode, RequestOptions } from '${RUNTIME_PACKAGE}/lib/types';`,
56
+ `import { validate } from '${RUNTIME_PACKAGE}/lib/validation';`,
55
57
  `import { type SWRResponse } from 'swr';`,
56
58
  `import { ${this.getValidator(
57
59
  model,
@@ -2,7 +2,7 @@ import { Context, Generator } from '../types';
2
2
  import { Project } from 'ts-morph';
3
3
  import * as path from 'path';
4
4
  import colors from 'colors';
5
- import { INTERNAL_PACKAGE } from '../constants';
5
+ import { RUNTIME_PACKAGE } from '../constants';
6
6
 
7
7
  /**
8
8
  * Generates ZenStack service code
@@ -22,7 +22,7 @@ export default class ServiceGenerator implements Generator {
22
22
 
23
23
  sf.addStatements([
24
24
  `import { PrismaClient } from "../.prisma";`,
25
- `import { DefaultService } from "${INTERNAL_PACKAGE}";`,
25
+ `import { DefaultService } from "${RUNTIME_PACKAGE}/lib/service";`,
26
26
  ]);
27
27
 
28
28
  const cls = sf.addClass({
@@ -2,10 +2,11 @@
2
2
  * Supported Prisma db providers
3
3
  */
4
4
  export const SUPPORTED_PROVIDERS = [
5
+ 'sqlite',
5
6
  'postgresql',
6
7
  'mysql',
7
- 'sqlite',
8
8
  'sqlserver',
9
+ 'cockroachdb',
9
10
  ];
10
11
 
11
12
  /**
@@ -7,6 +7,30 @@ enum ReferentialAction {
7
7
  * Used with "onUpdate": updates the relation scalar fields if the referenced scalar fields of the dependent record are updated.
8
8
  */
9
9
  Cascade
10
+
11
+ /*
12
+ * Used with "onDelete": prevents the deletion if any referencing records exist.
13
+ * Used with "onUpdate": prevents the identifier of a referenced record from being changed.
14
+ */
15
+ Restrict
16
+
17
+ /*
18
+ * Similar to 'Restrict', the difference between the two is dependent on the database being used.
19
+ * See details: https://www.prisma.io/docs/concepts/components/prisma-schema/relations/referential-actions#noaction
20
+ */
21
+ NoAction
22
+
23
+ /*
24
+ * Used with "onDelete": the scalar field of the referencing object will be set to NULL.
25
+ * Used with "onUpdate": when updating the identifier of a referenced object, the scalar fields of the referencing objects will be set to NULL.
26
+ */
27
+ SetNull
28
+
29
+ /*
30
+ * Used with "onDelete": the scalar field of the referencing object will be set to the fields default value.
31
+ * Used with "onUpdate": the scalar field of the referencing object will be set to the fields default value.
32
+ */
33
+ SetDefault
10
34
  }
11
35
 
12
36
  /*
package/src/telemetry.ts CHANGED
@@ -5,6 +5,8 @@ import cuid from 'cuid';
5
5
  import * as os from 'os';
6
6
  import sleep from 'sleep-promise';
7
7
  import exitHook from 'async-exit-hook';
8
+ import { CliError } from './cli/cli-error';
9
+ import { CommanderError } from 'commander';
8
10
 
9
11
  /**
10
12
  * Telemetry events
@@ -57,6 +59,13 @@ export class Telemetry {
57
59
  // a small delay to ensure telemetry is sent
58
60
  await sleep(this.exitWait);
59
61
  }
62
+
63
+ if (err instanceof CliError || err instanceof CommanderError) {
64
+ // error already handled
65
+ } else {
66
+ throw err;
67
+ }
68
+
60
69
  process.exit(1);
61
70
  });
62
71
  }
@@ -0,0 +1,63 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { execSync } from './exec-utils';
4
+
5
+ type PackageManagers = 'npm' | 'yarn' | 'pnpm';
6
+
7
+ function getPackageManager(projectPath = '.'): PackageManagers {
8
+ if (fs.existsSync(path.join(projectPath, 'yarn.lock'))) {
9
+ return 'yarn';
10
+ } else if (fs.existsSync(path.join(projectPath, 'pnpm-lock.yaml'))) {
11
+ return 'pnpm';
12
+ } else {
13
+ return 'npm';
14
+ }
15
+ }
16
+
17
+ export function installPackage(
18
+ pkg: string,
19
+ dev: boolean,
20
+ pkgManager: PackageManagers | undefined = undefined,
21
+ projectPath = '.'
22
+ ) {
23
+ const manager = pkgManager ?? getPackageManager(projectPath);
24
+ console.log(`Installing package "${pkg}" with ${manager}`);
25
+ switch (manager) {
26
+ case 'yarn':
27
+ execSync(
28
+ `yarn add --cwd "${projectPath}" ${
29
+ dev ? ' --save-dev' : ''
30
+ } ${pkg}`
31
+ );
32
+ break;
33
+
34
+ case 'pnpm':
35
+ execSync(
36
+ `pnpm add -C "${projectPath}" ${
37
+ dev ? ' --save-dev' : ''
38
+ } ${pkg}`
39
+ );
40
+ break;
41
+
42
+ default:
43
+ execSync(
44
+ `npm install --prefix "${projectPath}" ${
45
+ dev ? ' --save-dev' : ''
46
+ } ${pkg}`
47
+ );
48
+ break;
49
+ }
50
+ }
51
+
52
+ export function ensurePackage(
53
+ pkg: string,
54
+ dev: boolean,
55
+ pkgManager: PackageManagers | undefined = undefined,
56
+ projectPath = '.'
57
+ ) {
58
+ try {
59
+ require(pkg);
60
+ } catch {
61
+ installPackage(pkg, dev, pkgManager, projectPath);
62
+ }
63
+ }